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.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -148,23 +148,35 @@
.saturating_mul(writes),
))
}
-
- pub fn save(self) -> DispatchResult {
+ pub fn save(self) -> Result<(), DispatchError> {
+ self.check_is_read_only()?;
<CollectionById<T>>::insert(self.id, self.collection);
Ok(())
}
- pub fn set_sponsor(&mut self, sponsor: T::AccountId) {
+ pub fn set_sponsor(&mut self, sponsor: T::AccountId) -> DispatchResult {
+ self.check_is_read_only()?;
self.collection.sponsorship = SponsorshipState::Unconfirmed(sponsor);
+ Ok(())
}
- pub fn confirm_sponsorship(&mut self, sender: &T::AccountId) -> bool {
+ pub fn confirm_sponsorship(&mut self, sender: &T::AccountId) -> Result<bool, DispatchError> {
+ self.check_is_read_only()?;
+
if self.collection.sponsorship.pending_sponsor() != Some(sender) {
- return false;
- };
+ return Ok(false);
+ }
self.collection.sponsorship = SponsorshipState::Confirmed(sender.clone());
- true
+ Ok(true)
+ }
+
+ pub fn check_is_read_only(&self) -> DispatchResult {
+ if self.read_only {
+ return Err(<Error<T>>::CollectionNotFound)?;
+ }
+
+ Ok(())
}
}
@@ -434,6 +446,9 @@
/// Empty property keys are forbidden
EmptyPropertyKey,
+
+ /// Collection is read only
+ CollectionIsReadOnly,
}
#[pallet::storage]
@@ -669,6 +684,7 @@
sponsorship,
limits,
permissions,
+ read_only,
} = <CollectionById<T>>::get(collection)?;
let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)
@@ -698,6 +714,7 @@
permissions,
token_property_permissions,
properties,
+ read_only,
})
}
}
@@ -778,6 +795,7 @@
Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)
})
.unwrap_or_else(|| Ok(CollectionPermissions::default()))?,
+ read_only: false,
};
let mut collection_properties = up_data_structs::CollectionProperties::get();
@@ -834,6 +852,7 @@
collection: CollectionHandle<T>,
sender: &T::CrossAccountId,
) -> DispatchResult {
+ collection.check_is_read_only()?;
ensure!(
collection.limits.owner_can_destroy(),
<Error<T>>::NoPermission,
@@ -863,6 +882,7 @@
sender: &T::CrossAccountId,
property: Property,
) -> DispatchResult {
+ collection.check_is_read_only()?;
collection.check_is_owner_or_admin(sender)?;
CollectionProperties::<T>::try_mutate(collection.id, |properties| {
@@ -908,6 +928,8 @@
sender: &T::CrossAccountId,
properties: Vec<Property>,
) -> DispatchResult {
+ collection.check_is_read_only()?;
+
for property in properties {
Self::set_collection_property(collection, sender, property)?;
}
@@ -920,6 +942,7 @@
sender: &T::CrossAccountId,
property_key: PropertyKey,
) -> DispatchResult {
+ collection.check_is_read_only()?;
collection.check_is_owner_or_admin(sender)?;
CollectionProperties::<T>::try_mutate(collection.id, |properties| {
@@ -941,6 +964,8 @@
sender: &T::CrossAccountId,
property_keys: Vec<PropertyKey>,
) -> DispatchResult {
+ collection.check_is_read_only()?;
+
for key in property_keys {
Self::delete_collection_property(collection, sender, key)?;
}
@@ -965,6 +990,7 @@
sender: &T::CrossAccountId,
property_permission: PropertyKeyPermission,
) -> DispatchResult {
+ collection.check_is_read_only()?;
collection.check_is_owner_or_admin(sender)?;
let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);
@@ -996,6 +1022,8 @@
sender: &T::CrossAccountId,
property_permissions: Vec<PropertyKeyPermission>,
) -> DispatchResult {
+ collection.check_is_read_only()?;
+
for prop_pemission in property_permissions {
Self::set_property_permission(collection, sender, prop_pemission)?;
}
@@ -1083,6 +1111,7 @@
user: &T::CrossAccountId,
allowed: bool,
) -> DispatchResult {
+ collection.check_is_read_only()?;
collection.check_is_owner_or_admin(sender)?;
// =========
@@ -1102,6 +1131,7 @@
user: &T::CrossAccountId,
admin: bool,
) -> DispatchResult {
+ collection.check_is_read_only()?;
collection.check_is_owner_or_admin(sender)?;
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.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.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():', () => {