difftreelog
CORE-390 Add read only flag
in: master
8 files changed
pallets/common/src/erc.rsdiffbeforeafterboth87 check_is_owner(caller, self)?;87 check_is_owner(caller, self)?;888889 let sponsor = T::CrossAccountId::from_eth(sponsor);89 let sponsor = T::CrossAccountId::from_eth(sponsor);90 self.set_sponsor(sponsor.as_sub().clone());90 self.set_sponsor(sponsor.as_sub().clone()).map_err(dispatch_to_evm::<T>)?;91 save(self);91 save(self)92 Ok(())93 }92 }949395 fn confirm_collection_sponsorship(&mut self, caller: caller) -> Result<void> {94 fn confirm_collection_sponsorship(&mut self, caller: caller) -> Result<void> {96 let caller = T::CrossAccountId::from_eth(caller);95 let caller = T::CrossAccountId::from_eth(caller);97 if !self.confirm_sponsorship(caller.as_sub()) {96 if !self.confirm_sponsorship(caller.as_sub()).map_err(dispatch_to_evm::<T>)? {98 return Err(Error::Revert("Caller is not set as sponsor".into()));97 return Err(Error::Revert("Caller is not set as sponsor".into()));99 }98 }100 save(self);99 save(self)101 Ok(())102 }100 }103101104 #[solidity(rename_selector = "setCollectionLimit")]102 #[solidity(rename_selector = "setCollectionLimit")]134 }132 }135 self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)133 self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)136 .map_err(dispatch_to_evm::<T>)?;134 .map_err(dispatch_to_evm::<T>)?;137 save(self);135 save(self)138 Ok(())139 }136 }140137141 #[solidity(rename_selector = "setCollectionLimit")]138 #[solidity(rename_selector = "setCollectionLimit")]162 }159 }163 self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)160 self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)164 .map_err(dispatch_to_evm::<T>)?;161 .map_err(dispatch_to_evm::<T>)?;165 save(self);162 save(self)166 Ok(())167 }163 }168164169 fn contract_address(&self, _caller: caller) -> Result<address> {165 fn contract_address(&self, _caller: caller) -> Result<address> {296 }292 }297}293}298294299fn check_is_owner<T: Config>(caller: caller, collection: &CollectionHandle<T>) -> Result<()> {295fn check_is_owner<T: Config>(caller: caller, collection: &CollectionHandle<T>) -> Result<void> {300 let caller = T::CrossAccountId::from_eth(caller);296 let caller = T::CrossAccountId::from_eth(caller);301 collection297 collection302 .check_is_owner(&caller)298 .check_is_owner(&caller)315 Ok(caller)311 Ok(caller)316}312}317313318fn save<T: Config>(collection: &CollectionHandle<T>) {314fn save<T: Config>(collection: &CollectionHandle<T>) -> Result<void> {315 collection.check_is_read_only().map_err(dispatch_to_evm::<T>)?;319 <crate::CollectionById<T>>::insert(collection.id, collection.collection.clone());316 <crate::CollectionById<T>>::insert(collection.id, collection.collection.clone());317 Ok(())320}318}321319322pub fn token_uri_key() -> up_data_structs::PropertyKey {320pub fn token_uri_key() -> up_data_structs::PropertyKey {pallets/common/src/lib.rsdiffbeforeafterboth149 ))149 ))150 }150 }151152 pub fn save(self) -> DispatchResult {151 pub fn save(self) -> Result<(), DispatchError> {152 self.check_is_read_only()?;153 <CollectionById<T>>::insert(self.id, self.collection);153 <CollectionById<T>>::insert(self.id, self.collection);154 Ok(())154 Ok(())155 }155 }156156157 pub fn set_sponsor(&mut self, sponsor: T::AccountId) {157 pub fn set_sponsor(&mut self, sponsor: T::AccountId) -> DispatchResult {158 self.check_is_read_only()?;158 self.collection.sponsorship = SponsorshipState::Unconfirmed(sponsor);159 self.collection.sponsorship = SponsorshipState::Unconfirmed(sponsor);160 Ok(())159 }161 }160162161 pub fn confirm_sponsorship(&mut self, sender: &T::AccountId) -> bool {163 pub fn confirm_sponsorship(&mut self, sender: &T::AccountId) -> Result<bool, DispatchError> {164 self.check_is_read_only()?;165162 if self.collection.sponsorship.pending_sponsor() != Some(sender) {166 if self.collection.sponsorship.pending_sponsor() != Some(sender) {163 return false;167 return Ok(false);164 };168 }165169166 self.collection.sponsorship = SponsorshipState::Confirmed(sender.clone());170 self.collection.sponsorship = SponsorshipState::Confirmed(sender.clone());167 true171 Ok(true)168 }172 }173174 pub fn check_is_read_only(&self) -> DispatchResult {175 if self.read_only {176 return Err(<Error<T>>::CollectionNotFound)?;177 }178 179 Ok(())180 }169}181}170182171impl<T: Config> Deref for CollectionHandle<T> {183impl<T: Config> Deref for CollectionHandle<T> {435 /// Empty property keys are forbidden447 /// Empty property keys are forbidden436 EmptyPropertyKey,448 EmptyPropertyKey,449450 /// Collection is read only451 CollectionIsReadOnly,437 }452 }438453439 #[pallet::storage]454 #[pallet::storage]669 sponsorship,684 sponsorship,670 limits,685 limits,671 permissions,686 permissions,687 read_only,672 } = <CollectionById<T>>::get(collection)?;688 } = <CollectionById<T>>::get(collection)?;673689674 let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)690 let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)698 permissions,714 permissions,699 token_property_permissions,715 token_property_permissions,700 properties,716 properties,717 read_only,701 })718 })702 }719 }703}720}778 Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)795 Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)779 })796 })780 .unwrap_or_else(|| Ok(CollectionPermissions::default()))?,797 .unwrap_or_else(|| Ok(CollectionPermissions::default()))?,798 read_only: false,781 };799 };782800783 let mut collection_properties = up_data_structs::CollectionProperties::get();801 let mut collection_properties = up_data_structs::CollectionProperties::get();834 collection: CollectionHandle<T>,852 collection: CollectionHandle<T>,835 sender: &T::CrossAccountId,853 sender: &T::CrossAccountId,836 ) -> DispatchResult {854 ) -> DispatchResult {855 collection.check_is_read_only()?;837 ensure!(856 ensure!(838 collection.limits.owner_can_destroy(),857 collection.limits.owner_can_destroy(),839 <Error<T>>::NoPermission,858 <Error<T>>::NoPermission,863 sender: &T::CrossAccountId,882 sender: &T::CrossAccountId,864 property: Property,883 property: Property,865 ) -> DispatchResult {884 ) -> DispatchResult {885 collection.check_is_read_only()?;866 collection.check_is_owner_or_admin(sender)?;886 collection.check_is_owner_or_admin(sender)?;867887868 CollectionProperties::<T>::try_mutate(collection.id, |properties| {888 CollectionProperties::<T>::try_mutate(collection.id, |properties| {908 sender: &T::CrossAccountId,928 sender: &T::CrossAccountId,909 properties: Vec<Property>,929 properties: Vec<Property>,910 ) -> DispatchResult {930 ) -> DispatchResult {931 collection.check_is_read_only()?;932911 for property in properties {933 for property in properties {912 Self::set_collection_property(collection, sender, property)?;934 Self::set_collection_property(collection, sender, property)?;920 sender: &T::CrossAccountId,942 sender: &T::CrossAccountId,921 property_key: PropertyKey,943 property_key: PropertyKey,922 ) -> DispatchResult {944 ) -> DispatchResult {945 collection.check_is_read_only()?;923 collection.check_is_owner_or_admin(sender)?;946 collection.check_is_owner_or_admin(sender)?;924947925 CollectionProperties::<T>::try_mutate(collection.id, |properties| {948 CollectionProperties::<T>::try_mutate(collection.id, |properties| {941 sender: &T::CrossAccountId,964 sender: &T::CrossAccountId,942 property_keys: Vec<PropertyKey>,965 property_keys: Vec<PropertyKey>,943 ) -> DispatchResult {966 ) -> DispatchResult {967 collection.check_is_read_only()?;968944 for key in property_keys {969 for key in property_keys {945 Self::delete_collection_property(collection, sender, key)?;970 Self::delete_collection_property(collection, sender, key)?;965 sender: &T::CrossAccountId,990 sender: &T::CrossAccountId,966 property_permission: PropertyKeyPermission,991 property_permission: PropertyKeyPermission,967 ) -> DispatchResult {992 ) -> DispatchResult {993 collection.check_is_read_only()?;968 collection.check_is_owner_or_admin(sender)?;994 collection.check_is_owner_or_admin(sender)?;969995970 let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);996 let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);996 sender: &T::CrossAccountId,1022 sender: &T::CrossAccountId,997 property_permissions: Vec<PropertyKeyPermission>,1023 property_permissions: Vec<PropertyKeyPermission>,998 ) -> DispatchResult {1024 ) -> DispatchResult {1025 collection.check_is_read_only()?;1026999 for prop_pemission in property_permissions {1027 for prop_pemission in property_permissions {1000 Self::set_property_permission(collection, sender, prop_pemission)?;1028 Self::set_property_permission(collection, sender, prop_pemission)?;1083 user: &T::CrossAccountId,1111 user: &T::CrossAccountId,1084 allowed: bool,1112 allowed: bool,1085 ) -> DispatchResult {1113 ) -> DispatchResult {1114 collection.check_is_read_only()?;1086 collection.check_is_owner_or_admin(sender)?;1115 collection.check_is_owner_or_admin(sender)?;108711161088 // =========1117 // =========1102 user: &T::CrossAccountId,1131 user: &T::CrossAccountId,1103 admin: bool,1132 admin: bool,1104 ) -> DispatchResult {1133 ) -> DispatchResult {1134 collection.check_is_read_only()?;1105 collection.check_is_owner_or_admin(sender)?;1135 collection.check_is_owner_or_admin(sender)?;110611361107 let was_admin = <IsAdmin<T>>::get((collection.id, user));1137 let was_admin = <IsAdmin<T>>::get((collection.id, user));pallets/fungible/src/lib.rsdiffbeforeafterboth168 owner: &T::CrossAccountId,168 owner: &T::CrossAccountId,169 amount: u128,169 amount: u128,170 ) -> DispatchResult {170 ) -> DispatchResult {171 collection.check_is_read_only()?;172171 let total_supply = <TotalSupply<T>>::get(collection.id)173 let total_supply = <TotalSupply<T>>::get(collection.id)172 .checked_sub(amount)174 .checked_sub(amount)214 amount: u128,216 amount: u128,215 nesting_budget: &dyn Budget,217 nesting_budget: &dyn Budget,216 ) -> DispatchResult {218 ) -> DispatchResult {219 collection.check_is_read_only()?;220217 ensure!(221 ensure!(218 collection.limits.transfers_enabled(),222 collection.limits.transfers_enabled(),283 data: BTreeMap<T::CrossAccountId, u128>,287 data: BTreeMap<T::CrossAccountId, u128>,284 nesting_budget: &dyn Budget,288 nesting_budget: &dyn Budget,285 ) -> DispatchResult {289 ) -> DispatchResult {290 collection.check_is_read_only()?;291286 if !collection.is_owner_or_admin(sender) {292 if !collection.is_owner_or_admin(sender) {287 ensure!(293 ensure!(384 spender: &T::CrossAccountId,390 spender: &T::CrossAccountId,385 amount: u128,391 amount: u128,386 ) -> DispatchResult {392 ) -> DispatchResult {393 collection.check_is_read_only()?;387 if collection.permissions.access() == AccessMode::AllowList {394 if collection.permissions.access() == AccessMode::AllowList {388 collection.check_allowlist(owner)?;395 collection.check_allowlist(owner)?;389 collection.check_allowlist(spender)?;396 collection.check_allowlist(spender)?;pallets/nonfungible/src/lib.rsdiffbeforeafterboth336 sender: &T::CrossAccountId,336 sender: &T::CrossAccountId,337 token: TokenId,337 token: TokenId,338 ) -> DispatchResult {338 ) -> DispatchResult {339 collection.check_is_read_only()?;340339 let token_data =341 let token_data =340 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;342 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;456 &property.key,458 &property.key,457 is_token_create,459 is_token_create,458 )?;460 )?;461 collection.check_is_read_only()?;459462460 <TokenProperties<T>>::try_mutate((collection.id, token_id), |properties| {463 <TokenProperties<T>>::try_mutate((collection.id, token_id), |properties| {461 let property = property.clone();464 let property = property.clone();494 property_key: PropertyKey,497 property_key: PropertyKey,495 ) -> DispatchResult {498 ) -> DispatchResult {496 Self::check_token_change_permission(collection, sender, token_id, &property_key, false)?;499 Self::check_token_change_permission(collection, sender, token_id, &property_key, false)?;500 collection.check_is_read_only()?;497501498 <TokenProperties<T>>::try_mutate((collection.id, token_id), |properties| {502 <TokenProperties<T>>::try_mutate((collection.id, token_id), |properties| {499 properties.remove(&property_key)503 properties.remove(&property_key)570 token_id: TokenId,574 token_id: TokenId,571 property_keys: Vec<PropertyKey>,575 property_keys: Vec<PropertyKey>,572 ) -> DispatchResult {576 ) -> DispatchResult {577 collection.check_is_read_only()?;578573 for key in property_keys {579 for key in property_keys {574 Self::delete_token_property(collection, sender, token_id, key)?;580 Self::delete_token_property(collection, sender, token_id, key)?;616 token: TokenId,622 token: TokenId,617 nesting_budget: &dyn Budget,623 nesting_budget: &dyn Budget,618 ) -> DispatchResult {624 ) -> DispatchResult {625 collection.check_is_read_only()?;626619 ensure!(627 ensure!(620 collection.limits.transfers_enabled(),628 collection.limits.transfers_enabled(),894 token: TokenId,902 token: TokenId,895 spender: Option<&T::CrossAccountId>,903 spender: Option<&T::CrossAccountId>,896 ) -> DispatchResult {904 ) -> DispatchResult {905 collection.check_is_read_only()?;906 897 if collection.permissions.access() == AccessMode::AllowList {907 if collection.permissions.access() == AccessMode::AllowList {898 collection.check_allowlist(sender)?;908 collection.check_allowlist(sender)?;pallets/refungible/src/lib.rsdiffbeforeafterboth234 }234 }235235236 pub fn burn_token(collection: &RefungibleHandle<T>, token_id: TokenId) -> DispatchResult {236 pub fn burn_token(collection: &RefungibleHandle<T>, token_id: TokenId) -> DispatchResult {237 collection.check_is_read_only()?;237 let burnt = <TokensBurnt<T>>::get(collection.id)238 let burnt = <TokensBurnt<T>>::get(collection.id)238 .checked_add(1)239 .checked_add(1)239 .ok_or(ArithmeticError::Overflow)?;240 .ok_or(ArithmeticError::Overflow)?;253 token: TokenId,254 token: TokenId,254 amount: u128,255 amount: u128,255 ) -> DispatchResult {256 ) -> DispatchResult {257 collection.check_is_read_only()?;256 let total_supply = <TotalSupply<T>>::get((collection.id, token))258 let total_supply = <TotalSupply<T>>::get((collection.id, token))257 .checked_sub(amount)259 .checked_sub(amount)258 .ok_or(<CommonError<T>>::TokenValueTooLow)?;260 .ok_or(<CommonError<T>>::TokenValueTooLow)?;325 amount: u128,327 amount: u128,326 nesting_budget: &dyn Budget,328 nesting_budget: &dyn Budget,327 ) -> DispatchResult {329 ) -> DispatchResult {330 collection.check_is_read_only()?;328 ensure!(331 ensure!(329 collection.limits.transfers_enabled(),332 collection.limits.transfers_enabled(),330 <CommonError<T>>::TransferNotAllowed333 <CommonError<T>>::TransferNotAllowed573 token: TokenId,576 token: TokenId,574 amount: u128,577 amount: u128,575 ) -> DispatchResult {578 ) -> DispatchResult {579 collection.check_is_read_only()?;576 if collection.permissions.access() == AccessMode::AllowList {580 if collection.permissions.access() == AccessMode::AllowList {577 collection.check_allowlist(sender)?;581 collection.check_allowlist(sender)?;578 collection.check_allowlist(spender)?;582 collection.check_allowlist(spender)?;pallets/unique/src/lib.rsdiffbeforeafterboth304 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {304 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {305 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);305 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);306 let collection = <CollectionHandle<T>>::try_get(collection_id)?;306 let collection = <CollectionHandle<T>>::try_get(collection_id)?;307 collection.check_is_read_only()?;307308308 // =========309 // =========309310406 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);407 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);407408408 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;409 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;410 target_collection.check_is_read_only()?;409 target_collection.check_is_owner(&sender)?;411 target_collection.check_is_owner(&sender)?;410412411 target_collection.owner = new_owner.clone();413 target_collection.owner = new_owner.clone();487 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;489 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;488 target_collection.check_is_owner(&sender)?;490 target_collection.check_is_owner(&sender)?;489491490 target_collection.set_sponsor(new_sponsor.clone());492 target_collection.set_sponsor(new_sponsor.clone())?;491493492 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(494 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(493 collection_id,495 collection_id,511513512 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;514 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;513 ensure!(515 ensure!(514 target_collection.confirm_sponsorship(&sender),516 target_collection.confirm_sponsorship(&sender)?,515 Error::<T>::ConfirmUnsetSponsorFail517 Error::<T>::ConfirmUnsetSponsorFail516 );518 );517519primitives/data-structs/src/lib.rsdiffbeforeafterboth316 #[version(2.., upper(Default::default()))]316 #[version(2.., upper(Default::default()))]317 pub permissions: CollectionPermissions,317 pub permissions: CollectionPermissions,318319 #[version(2.., upper(false))]320 pub read_only: bool,318321319 #[version(..2)]322 #[version(..2)]320 pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,323 pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,340 pub permissions: CollectionPermissions,343 pub permissions: CollectionPermissions,341 pub token_property_permissions: Vec<PropertyKeyPermission>,344 pub token_property_permissions: Vec<PropertyKeyPermission>,342 pub properties: Vec<Property>,345 pub properties: Vec<Property>,346 pub read_only: bool,343}347}344348345#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Derivative, MaxEncodedLen)]349#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Derivative, MaxEncodedLen)]tests/src/createCollection.test.tsdiffbeforeafterboth88 });88 });89 });89 });9091 it('Create new collection is not read only', async () => {92 await usingApi(async api => {93 const alice = privateKey('//Alice');94 const tx = api.tx.unique.createCollectionEx({95 readOnly: true96 });97 const events = await submitTransactionAsync(alice, tx);98 const result = getCreateCollectionResult(events);99100 const collection = (await getDetailedCollectionInfo(api, result.collectionId))!;101 expect(collection.readOnly.toHuman()).to.be.false;102 });103 });90});104});9110592describe('(!negative test!) integration test: ext. createCollection():', () => {106describe('(!negative test!) integration test: ext. createCollection():', () => {