Repository: public-awesome/launchpad (rust)

Issue:
check royalties
royalties can only be decreased

Pull request description:
check royalties
fix #398

Patch:
diff --git a/contracts/sg721-base/src/contract.rs b/contracts/sg721-base/src/contract.rs
index 1ef705c6..528b559a 100644
--- a/contracts/sg721-base/src/contract.rs
+++ b/contracts/sg721-base/src/contract.rs
@@ -225,13 +225,6 @@ where
             return Err(ContractError::Unauthorized {});
         }
 
-        // convert collection royalty info to response for comparison
-        // convert from response to royalty info for storage
-        let royalty_info_res = collection
-            .royalty_info
-            .as_ref()
-            .map(|royalty_info| royalty_info.to_response());
-
         collection.description = collection_msg
             .description
             .unwrap_or_else(|| collection.description.to_string());
@@ -249,14 +242,32 @@ where
             .unwrap_or_else(|| collection.external_link.as_ref().map(|s| s.to_string()));
         Url::parse(collection.external_link.as_ref().unwrap())?;
 
-        let response = collection_msg.royalty_info.unwrap_or(royalty_info_res);
+        // convert collection royalty info to response for comparison
+        // convert from response to royalty info for storage
+        let royalty_info_res = collection
+            .royalty_info
+            .as_ref()
+            .map(|royalty_info| royalty_info.to_response());
 
-        collection.royalty_info = match response {
-            Some(royalty_info) => Some(RoyaltyInfo {
+        let response = collection_msg
+            .royalty_info
+            .unwrap_or_else(|| royalty_info_res.clone());
+
+        // reminder: collection_msg.royalty_info is Option<Option<RoyaltyInfoResponse>>
+        collection.royalty_info = if let Some(royalty_info) = response {
+            // update royalty info to equal or less, else throw error
+            if let Some(royalty_info_res) = royalty_info_res {
+                if royalty_info.share > royalty_info_res.share {
+                    return Err(ContractError::RoyaltyShareIncreased {});
+                }
+            }
+
+            Some(RoyaltyInfo {
                 payment_address: deps.api.addr_validate(&royalty_info.payment_address)?,
                 share: share_validate(royalty_info.share)?,
-            }),
-            None => None,
+            })
+        } else {
+            None
         };
 
         self.collection_info.save(deps.storage, &collection)?;
diff --git a/contracts/sg721-base/src/error.rs b/contracts/sg721-base/src/error.rs
index 5d5f364c..3ae294f6 100644
--- a/contracts/sg721-base/src/error.rs
+++ b/contracts/sg721-base/src/error.rs
@@ -47,6 +47,9 @@ pub enum ContractError {
 
     #[error("CollectionInfoFrozen")]
     CollectionInfoFrozen {},
+
+    #[error("RoyaltyShareIncreased")]
+    RoyaltyShareIncreased {},
 }
 
 impl From<ContractError> for Cw721ContractError {


Test patch:
diff --git a/contracts/sg721-base/src/integration_tests.rs b/contracts/sg721-base/src/integration_tests.rs
index bef1fd52..653c15da 100644
--- a/contracts/sg721-base/src/integration_tests.rs
+++ b/contracts/sg721-base/src/integration_tests.rs
@@ -247,7 +247,7 @@ mod tests {
             // update royalty_info
             let royalty_info: Option<RoyaltyInfoResponse> = Some(RoyaltyInfoResponse {
                 payment_address: creator.to_string(),
-                share: Decimal::percent(10 / 100),
+                share: Decimal::percent(10),
             });
             let res = app.execute_contract(
                 creator.clone(),
@@ -270,6 +270,26 @@ mod tests {
                 .unwrap();
             assert_eq!(res.royalty_info.unwrap(), royalty_info.unwrap());
 
+            // try update royalty_info higher
+            let royalty_info: Option<RoyaltyInfoResponse> = Some(RoyaltyInfoResponse {
+                payment_address: creator.to_string(),
+                share: Decimal::percent(11),
+            });
+            let res = app.execute_contract(
+                creator.clone(),
+                contract.clone(),
+                &Sg721ExecuteMsg::<Empty>::UpdateCollectionInfo {
+                    collection_info: UpdateCollectionInfoMsg {
+                        description: None,
+                        image: None,
+                        external_link: None,
+                        royalty_info: Some(royalty_info),
+                    },
+                },
+                &[],
+            );
+            assert!(res.is_err());
+
             // freeze collection throw err if not creator
             let res = app.execute_contract(
                 Addr::unchecked("badguy"),