Repository: CosmWasm/sylvia
Pull request #438 (2024-09-27 12:35:56)

feat: Add `UpdateAdmin` and `ClearAdmin` constructors in the `Remote`

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/CosmWasm/sylvia/pull/438?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm) Report
Attention: Patch coverage is `0%` with `19 lines` in your changes missing coverage. Please review.
> Project coverage is 71.86%. Comparing base [(`8140714`)](https://app.codecov.io/gh/CosmWasm/sylvia/commit/81407141135767631899a532bfb19c1a5474f177?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm) to head [(`735bfeb`)](https://app.codecov.io/gh/CosmWasm/sylvia/commit/735bfeb1c9c1435a9d6c827d357ac8a30685c1ab?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm).

| [Files with missing lines](https://app.codecov.io/gh/CosmWasm/sylvia/pull/438?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm) | Patch % | Lines |
|---|---|---|
| [sylvia/tests/remote.rs](https://app.codecov.io/gh/CosmWasm/sylvia/pull/438?src=pr&el=tree&filepath=sylvia%2Ftests%2Fremote.rs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm#diff-c3lsdmlhL3Rlc3RzL3JlbW90ZS5ycw==) | 0.00% | [15 Missing :warning: ](https://app.codecov.io/gh/CosmWasm/sylvia/pull/438?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm) |
| [sylvia/src/types.rs](https://app.codecov.io/gh/CosmWasm/sylvia/pull/438?src=pr&el=tree&filepath=sylvia%2Fsrc%2Ftypes.rs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm#diff-c3lsdmlhL3NyYy90eXBlcy5ycw==) | 0.00% | [4 Missing :warning: ](https://app.codecov.io/gh/CosmWasm/sylvia/pull/438?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm) |

<details><summary>Additional details and impacted files</summary>


```diff
@@                       Coverage Diff                        @@
##           jawoznia/feat/submsg_builder     #438      +/-   ##
================================================================
- Coverage                         72.26%   71.86%   -0.40%     
================================================================
  Files                                56       56              
  Lines                              3447     3466      +19     
================================================================
  Hits                               2491     2491              
- Misses                              956      975      +19     
```

| [Flag](https://app.codecov.io/gh/CosmWasm/sylvia/pull/438/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm) | Coverage Δ | |
|---|---|---|
| [](https://app.codecov.io/gh/CosmWasm/sylvia/pull/438/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm) | `71.86% <0.00%> (-0.40%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm#carryforward-flags-in-the-pull-request-comment) to find out more.

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/CosmWasm/sylvia/pull/438?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm).

jawoznia: I agree it could be simplified


>     2. Names `signed` and `unsigned` reminds me of signing something with some cryptographic tool, when in this context it means `i32` and `u32`. Let's change it to `CounterI32`/`CounterU32` or `ContractI32`/`ContractU32` instead.

I can see that. Let's change it later.
 
>     3. `ContractStorage` is a dead code - to be removed.

This is intentional. There was a bug where it was impossible to store `Remote` in custom storage types. A comment explaining this would be very much in place.

>     4. Remove the `#[entry_points(generics<i32>)]` - we don't need it in the test scenarios. Moreover it might be misleading for other devs:

It provides the coverage though.
I would prefer to move all the bloated examples to the tests and make them act as the coverage.
The examples should be refactored to provide simple single feature showcases.

Patch:
diff --git a/sylvia/src/types.rs b/sylvia/src/types.rs
index cbf12d3..b3999c6 100644
--- a/sylvia/src/types.rs
+++ b/sylvia/src/types.rs
@@ -1,4 +1,5 @@
 //! Module providing utilities to build and use sylvia contracts.
+
 use cosmwasm_std::{Binary, Coin, Deps, DepsMut, Empty, Env, MessageInfo, WasmMsg};
 #[cfg(feature = "sv_replies")]
 use cosmwasm_std::{Event, MsgResponse};
@@ -431,9 +432,25 @@ impl<'a, Contract: ?Sized> Remote<'a, Contract> {
         }
     }
 
+    /// Creates a new instance of [ExecutorBuilder] from underlying contract address.
     pub fn executor(&self) -> ExecutorBuilder<(EmptyExecutorBuilderState, Contract)> {
         ExecutorBuilder::<(EmptyExecutorBuilderState, Contract)>::new(&self.addr)
     }
+
+    /// Creates a new instance of [WasmMsg::UpdateAdmin] from underlying contract address and provided admin address.
+    pub fn update_admin(&self, new_admin: &str) -> WasmMsg {
+        WasmMsg::UpdateAdmin {
+            contract_addr: self.addr.to_string(),
+            admin: new_admin.to_string(),
+        }
+    }
+
+    /// Creates a new instance of [WasmMsg::ClearAdmin] from underlying contract address.
+    pub fn clear_admin(&self) -> WasmMsg {
+        WasmMsg::ClearAdmin {
+            contract_addr: self.addr.to_string(),
+        }
+    }
 }
 
 impl<'a, Contract: ?Sized> AsRef<cosmwasm_std::Addr> for Remote<'a, Contract> {


Test patch:
diff --git a/sylvia/tests/remote.rs b/sylvia/tests/remote.rs
index 1c43322..f7468e6 100644
--- a/sylvia/tests/remote.rs
+++ b/sylvia/tests/remote.rs
@@ -156,7 +156,9 @@ pub mod unsigned_contract {
     }
 }
 
-// Making sure `Remote` can be stored in `#[cw_serde]` types
+// Making sure `Remote` can be stored in `#[cw_serde]` types.
+// This is intentionally a dead code.
+// https://github.com/CosmWasm/sylvia/issues/181
 #[cw_serde]
 #[allow(dead_code)]
 pub struct ContractStorage<Contract> {
@@ -257,12 +259,52 @@ pub mod manager {
 
             Ok(count)
         }
+
+        #[sv::msg(exec)]
+        fn update_admin(
+            &self,
+            ctx: ExecCtx<ExampleQuery>,
+            new_admin: String,
+        ) -> Result<Response<ExampleMsg>, StdError> {
+            let wasm = self
+                .remote_counter
+                .load(ctx.deps.storage)?
+                .interface_remote
+                .update_admin(&new_admin);
+            let resp = Response::new().add_message(wasm);
+            Ok(resp)
+        }
+
+        #[sv::msg(exec)]
+        fn clear_admin(
+            &self,
+            ctx: ExecCtx<ExampleQuery>,
+        ) -> Result<Response<ExampleMsg>, StdError> {
+            let wasm = self
+                .remote_counter
+                .load(ctx.deps.storage)?
+                .interface_remote
+                .clear_admin();
+            let resp = Response::new().add_message(wasm);
+            Ok(resp)
+        }
+
+        #[sv::msg(query)]
+        fn counter_contract(&self, ctx: QueryCtx<ExampleQuery>) -> Result<Addr, StdError> {
+            Ok(self
+                .remote_counter
+                .load(ctx.deps.storage)?
+                .interface_remote
+                .as_ref()
+                .clone())
+        }
     }
 }
 
 #[cfg(test)]
 mod tests {
-    use cw_multi_test::{BasicApp, IntoBech32};
+    use cosmwasm_std::{CosmosMsg, WasmMsg};
+    use cw_multi_test::{BasicApp, Executor, IntoAddr};
     use sylvia::cw_std::{Addr, StdError};
     use sylvia::multitest::{App, Proxy};
     use sylvia::types::Remote;
@@ -277,7 +319,6 @@ mod tests {
     use crate::{ExampleMsg, ExampleQuery};
 
     type ExampleApp = BasicApp<ExampleMsg, ExampleQuery>;
-    const OWNER: &str = "owner";
 
     #[test]
     fn remote_generation() {
@@ -302,11 +343,12 @@ mod tests {
         assert_eq!(&addr, borrowed_remote.as_ref());
     }
 
-    fn setup(
-        app: &App<ExampleApp>,
+    fn setup<'a>(
+        app: &'a App<ExampleApp>,
+        owner: &'a Addr,
     ) -> (
-        Proxy<ExampleApp, ManagerContract<i32>>,
-        Proxy<ExampleApp, ManagerContract<u32>>,
+        Proxy<'a, ExampleApp, ManagerContract<i32>>,
+        Proxy<'a, ExampleApp, ManagerContract<u32>>,
     ) {
         // Manager operating on signed numbers
         let signed_counter_code_id = SignedCounterCodeId::store_code(app);
@@ -314,7 +356,7 @@ mod tests {
         let signed_counter_contract = signed_counter_code_id
             .instantiate()
             .with_label("Signed counter contract")
-            .call(&OWNER.into_bech32())
+            .call(owner)
             .unwrap();
 
         let manager_code_id = ManagerCodeId::store_code(app);
@@ -322,7 +364,7 @@ mod tests {
         let signed_manager_contract = manager_code_id
             .instantiate(signed_counter_contract.contract_addr.clone())
             .with_label("Manager contract")
-            .call(&OWNER.into_bech32())
+            .call(owner)
             .unwrap();
 
         // Manager operating on unsigned numbers
@@ -331,7 +373,8 @@ mod tests {
         let unsigned_counter_contract = unsigned_counter_code_id
             .instantiate()
             .with_label("Unsigned counter contract")
-            .call(&OWNER.into_bech32())
+            .with_admin(Some(owner.as_str()))
+            .call(owner)
             .unwrap();
 
         let manager_code_id = ManagerCodeId::store_code(app);
@@ -339,7 +382,18 @@ mod tests {
         let unsigned_manager_contract = manager_code_id
             .instantiate(unsigned_counter_contract.contract_addr.clone())
             .with_label("Manager contract")
-            .call(&OWNER.into_bech32())
+            .call(owner)
+            .unwrap();
+
+        // Set manager contract as an admin of the counter contract
+        app.app_mut()
+            .execute(
+                owner.clone(),
+                CosmosMsg::Wasm(WasmMsg::UpdateAdmin {
+                    contract_addr: unsigned_counter_contract.contract_addr.to_string(),
+                    admin: unsigned_manager_contract.contract_addr.to_string(),
+                }),
+            )
             .unwrap();
 
         (signed_manager_contract, unsigned_manager_contract)
@@ -347,23 +401,81 @@ mod tests {
 
     #[test]
     fn call_remote() {
+        let owner = "owner".into_addr();
         let app = App::<cw_multi_test::BasicApp<ExampleMsg, ExampleQuery>>::custom(|_, _, _| {});
-        let (signed_manager_contract, unsigned_manager_contract) = setup(&app);
+        let (signed_manager_contract, unsigned_manager_contract) = setup(&app, &owner);
 
         assert_eq!(signed_manager_contract.count().unwrap(), 0);
 
-        signed_manager_contract
-            .add(5)
-            .call(&OWNER.into_bech32())
-            .unwrap();
+        signed_manager_contract.add(5).call(&owner).unwrap();
         assert_eq!(signed_manager_contract.count().unwrap(), 5);
 
         assert_eq!(unsigned_manager_contract.count().unwrap(), 0);
 
+        unsigned_manager_contract.add(5).call(&owner).unwrap();
+        assert_eq!(unsigned_manager_contract.count().unwrap(), 5);
+    }
+
+    #[test]
+    fn update_admin() {
+        let owner = "owner".into_addr();
+        let app = App::<cw_multi_test::BasicApp<ExampleMsg, ExampleQuery>>::custom(|_, _, _| {});
+        let (_, unsigned_manager_contract) = setup(&app, &owner);
+        let new_admin = "new_admin".into_addr();
+
+        let unsigned_counter_contract_addr = unsigned_manager_contract.counter_contract().unwrap();
+
+        // Initial admin should be the manager_contract
+        let contract_info = app
+            .querier()
+            .query_wasm_contract_info(unsigned_counter_contract_addr.clone())
+            .unwrap();
+        assert_eq!(
+            contract_info.admin,
+            Some(unsigned_manager_contract.contract_addr.clone())
+        );
+
+        // Add new admin
         unsigned_manager_contract
-            .add(5)
-            .call(&OWNER.into_bech32())
+            .update_admin(new_admin.to_string())
+            .call(&owner)
             .unwrap();
-        assert_eq!(unsigned_manager_contract.count().unwrap(), 5);
+
+        let contract_info = app
+            .querier()
+            .query_wasm_contract_info(unsigned_counter_contract_addr)
+            .unwrap();
+        assert_eq!(contract_info.admin, Some(new_admin.clone()));
+    }
+
+    #[test]
+    fn clear_admin() {
+        let owner = "owner".into_addr();
+        let app = App::<cw_multi_test::BasicApp<ExampleMsg, ExampleQuery>>::custom(|_, _, _| {});
+        let (_, unsigned_manager_contract) = setup(&app, &owner);
+
+        let unsigned_counter_contract_addr = unsigned_manager_contract.counter_contract().unwrap();
+
+        // Initial admin should be the manager_contract
+        let contract_info = app
+            .querier()
+            .query_wasm_contract_info(unsigned_counter_contract_addr.clone())
+            .unwrap();
+        assert_eq!(
+            contract_info.admin,
+            Some(unsigned_manager_contract.contract_addr.clone())
+        );
+
+        // Clear admin
+        unsigned_manager_contract
+            .clear_admin()
+            .call(&owner)
+            .unwrap();
+
+        let contract_info = app
+            .querier()
+            .query_wasm_contract_info(unsigned_counter_contract_addr.clone())
+            .unwrap();
+        assert_eq!(contract_info.admin, None);
     }
 }