difftreelog
CORE-390 Add read only flag
in: master
8 files changed
pallets/common/src/erc.rsdiffbeforeafterboth--- a/pallets/common/src/erc.rs
+++ b/pallets/common/src/erc.rs
@@ -87,18 +87,16 @@
check_is_owner(caller, self)?;
let sponsor = T::CrossAccountId::from_eth(sponsor);
- self.set_sponsor(sponsor.as_sub().clone());
- save(self);
- Ok(())
+ self.set_sponsor(sponsor.as_sub().clone()).map_err(dispatch_to_evm::<T>)?;
+ save(self)
}
fn confirm_collection_sponsorship(&mut self, caller: caller) -> Result<void> {
let caller = T::CrossAccountId::from_eth(caller);
- if !self.confirm_sponsorship(caller.as_sub()) {
+ if !self.confirm_sponsorship(caller.as_sub()).map_err(dispatch_to_evm::<T>)? {
return Err(Error::Revert("Caller is not set as sponsor".into()));
}
- save(self);
- Ok(())
+ save(self)
}
#[solidity(rename_selector = "setCollectionLimit")]
@@ -134,8 +132,7 @@
}
self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)
.map_err(dispatch_to_evm::<T>)?;
- save(self);
- Ok(())
+ save(self)
}
#[solidity(rename_selector = "setCollectionLimit")]
@@ -162,8 +159,7 @@
}
self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)
.map_err(dispatch_to_evm::<T>)?;
- save(self);
- Ok(())
+ save(self)
}
fn contract_address(&self, _caller: caller) -> Result<address> {
@@ -296,7 +292,7 @@
}
}
-fn check_is_owner<T: Config>(caller: caller, collection: &CollectionHandle<T>) -> Result<()> {
+fn check_is_owner<T: Config>(caller: caller, collection: &CollectionHandle<T>) -> Result<void> {
let caller = T::CrossAccountId::from_eth(caller);
collection
.check_is_owner(&caller)
@@ -315,8 +311,10 @@
Ok(caller)
}
-fn save<T: Config>(collection: &CollectionHandle<T>) {
+fn save<T: Config>(collection: &CollectionHandle<T>) -> Result<void> {
+ collection.check_is_read_only().map_err(dispatch_to_evm::<T>)?;
<crate::CollectionById<T>>::insert(collection.id, collection.collection.clone());
+ Ok(())
}
pub 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.rsdiffbeforeafterboth--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -168,6 +168,8 @@
owner: &T::CrossAccountId,
amount: u128,
) -> DispatchResult {
+ collection.check_is_read_only()?;
+
let total_supply = <TotalSupply<T>>::get(collection.id)
.checked_sub(amount)
.ok_or(<CommonError<T>>::TokenValueTooLow)?;
@@ -214,6 +216,8 @@
amount: u128,
nesting_budget: &dyn Budget,
) -> DispatchResult {
+ collection.check_is_read_only()?;
+
ensure!(
collection.limits.transfers_enabled(),
<CommonError<T>>::TransferNotAllowed,
@@ -283,6 +287,8 @@
data: BTreeMap<T::CrossAccountId, u128>,
nesting_budget: &dyn Budget,
) -> DispatchResult {
+ collection.check_is_read_only()?;
+
if !collection.is_owner_or_admin(sender) {
ensure!(
collection.permissions.mint_mode(),
@@ -384,6 +390,7 @@
spender: &T::CrossAccountId,
amount: u128,
) -> DispatchResult {
+ collection.check_is_read_only()?;
if collection.permissions.access() == AccessMode::AllowList {
collection.check_allowlist(owner)?;
collection.check_allowlist(spender)?;
pallets/nonfungible/src/lib.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -336,6 +336,8 @@
sender: &T::CrossAccountId,
token: TokenId,
) -> DispatchResult {
+ collection.check_is_read_only()?;
+
let token_data =
<TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;
ensure!(
@@ -456,6 +458,7 @@
&property.key,
is_token_create,
)?;
+ collection.check_is_read_only()?;
<TokenProperties<T>>::try_mutate((collection.id, token_id), |properties| {
let property = property.clone();
@@ -494,6 +497,7 @@
property_key: PropertyKey,
) -> DispatchResult {
Self::check_token_change_permission(collection, sender, token_id, &property_key, false)?;
+ collection.check_is_read_only()?;
<TokenProperties<T>>::try_mutate((collection.id, token_id), |properties| {
properties.remove(&property_key)
@@ -570,6 +574,8 @@
token_id: TokenId,
property_keys: Vec<PropertyKey>,
) -> DispatchResult {
+ collection.check_is_read_only()?;
+
for key in property_keys {
Self::delete_token_property(collection, sender, token_id, key)?;
}
@@ -616,6 +622,8 @@
token: TokenId,
nesting_budget: &dyn Budget,
) -> DispatchResult {
+ collection.check_is_read_only()?;
+
ensure!(
collection.limits.transfers_enabled(),
<CommonError<T>>::TransferNotAllowed
@@ -894,6 +902,8 @@
token: TokenId,
spender: Option<&T::CrossAccountId>,
) -> DispatchResult {
+ collection.check_is_read_only()?;
+
if collection.permissions.access() == AccessMode::AllowList {
collection.check_allowlist(sender)?;
if let Some(spender) = spender {
pallets/refungible/src/lib.rsdiffbeforeafterboth--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -234,6 +234,7 @@
}
pub fn burn_token(collection: &RefungibleHandle<T>, token_id: TokenId) -> DispatchResult {
+ collection.check_is_read_only()?;
let burnt = <TokensBurnt<T>>::get(collection.id)
.checked_add(1)
.ok_or(ArithmeticError::Overflow)?;
@@ -253,6 +254,7 @@
token: TokenId,
amount: u128,
) -> DispatchResult {
+ collection.check_is_read_only()?;
let total_supply = <TotalSupply<T>>::get((collection.id, token))
.checked_sub(amount)
.ok_or(<CommonError<T>>::TokenValueTooLow)?;
@@ -325,6 +327,7 @@
amount: u128,
nesting_budget: &dyn Budget,
) -> DispatchResult {
+ collection.check_is_read_only()?;
ensure!(
collection.limits.transfers_enabled(),
<CommonError<T>>::TransferNotAllowed
@@ -573,6 +576,7 @@
token: TokenId,
amount: u128,
) -> DispatchResult {
+ collection.check_is_read_only()?;
if collection.permissions.access() == AccessMode::AllowList {
collection.check_allowlist(sender)?;
collection.check_allowlist(spender)?;
pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -304,6 +304,7 @@
pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let collection = <CollectionHandle<T>>::try_get(collection_id)?;
+ collection.check_is_read_only()?;
// =========
@@ -406,6 +407,7 @@
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;
+ target_collection.check_is_read_only()?;
target_collection.check_is_owner(&sender)?;
target_collection.owner = new_owner.clone();
@@ -487,7 +489,7 @@
let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;
target_collection.check_is_owner(&sender)?;
- target_collection.set_sponsor(new_sponsor.clone());
+ target_collection.set_sponsor(new_sponsor.clone())?;
<Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(
collection_id,
@@ -511,7 +513,7 @@
let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;
ensure!(
- target_collection.confirm_sponsorship(&sender),
+ target_collection.confirm_sponsorship(&sender)?,
Error::<T>::ConfirmUnsetSponsorFail
);
primitives/data-structs/src/lib.rsdiffbeforeafterboth--- a/primitives/data-structs/src/lib.rs
+++ b/primitives/data-structs/src/lib.rs
@@ -316,6 +316,9 @@
#[version(2.., upper(Default::default()))]
pub permissions: CollectionPermissions,
+ #[version(2.., upper(false))]
+ pub read_only: bool,
+
#[version(..2)]
pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,
@@ -340,6 +343,7 @@
pub permissions: CollectionPermissions,
pub token_property_permissions: Vec<PropertyKeyPermission>,
pub properties: Vec<Property>,
+ pub read_only: bool,
}
#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Derivative, MaxEncodedLen)]
tests/src/createCollection.test.tsdiffbeforeafterboth--- a/tests/src/createCollection.test.ts
+++ b/tests/src/createCollection.test.ts
@@ -87,6 +87,20 @@
expect(collection.limits.accountTokenOwnershipLimit.unwrap().toNumber()).to.equal(3);
});
});
+
+ it('Create new collection is not read only', async () => {
+ await usingApi(async api => {
+ const alice = privateKey('//Alice');
+ const tx = api.tx.unique.createCollectionEx({
+ readOnly: true
+ });
+ const events = await submitTransactionAsync(alice, tx);
+ const result = getCreateCollectionResult(events);
+
+ const collection = (await getDetailedCollectionInfo(api, result.collectionId))!;
+ expect(collection.readOnly.toHuman()).to.be.false;
+ });
+ });
});
describe('(!negative test!) integration test: ext. createCollection():', () => {