cw_blob/
lib.rs

1use cosmwasm_std::{DepsMut, Empty, Env, MessageInfo, Never, Response};
2
3/// Checksum of the wasm
4// Unused, so optimized out of the wasm
5pub const CHECKSUM: [u8; 32] = [
6    95, 107, 196, 86, 230, 56, 249, 93, 219, 111, 197, 12, 17, 82, 18, 222, 186, 194, 177, 42, 25,
7    7, 208, 121, 178, 254, 148, 61, 239, 125, 113, 217,
8];
9
10#[cfg_attr(not(feature = "library"), cosmwasm_std::entry_point)]
11pub fn instantiate(_: DepsMut, _: Env, _: MessageInfo, _: Empty) -> Result<Response, Never> {
12    Ok(Response::new())
13}
14
15#[cfg(not(target_arch = "wasm32"))]
16pub mod interface {
17    use super::*;
18
19    use cosmwasm_std::{
20        instantiate2_address, Binary, CanonicalAddr, Checksum, Instantiate2AddressError,
21    };
22    use cw_orch::{contract::Contract, prelude::*};
23
24    // We don't want it to be manually instantiated/executed/etc, only uploaded. So not using cw_orch interface
25    #[derive(Clone)]
26    pub struct CwBlob<Chain>(Contract<Chain>);
27
28    impl<Chain> CwBlob<Chain> {
29        pub fn new(contract_id: impl ToString, chain: Chain) -> Self {
30            Self(Contract::new(contract_id, chain))
31        }
32    }
33
34    impl<Chain: ChainState> ContractInstance<Chain> for CwBlob<Chain> {
35        fn as_instance(&self) -> &Contract<Chain> {
36            &self.0
37        }
38
39        fn as_instance_mut(&mut self) -> &mut Contract<Chain> {
40            &mut self.0
41        }
42    }
43
44    impl<T: CwEnv> Uploadable for CwBlob<T> {
45        fn wasm(_chain: &ChainInfoOwned) -> WasmPath {
46            wasm_path()
47        }
48
49        fn wrapper() -> Box<dyn MockContract<Empty, Empty>> {
50            Box::new(
51                ContractWrapper::new_with_empty(
52                    |_, _, _, _: Empty| -> Result<Response, Never> { unreachable!() },
53                    super::instantiate,
54                    |_, _, _: Empty| -> Result<Binary, Never> { unreachable!() },
55                )
56                .with_checksum(checksum()),
57            )
58        }
59    }
60
61    pub fn checksum() -> Checksum {
62        Checksum::from(CHECKSUM)
63    }
64
65    pub(crate) fn wasm_path() -> WasmPath {
66        artifacts_dir_from_workspace!()
67            .find_wasm_path("cw_blob")
68            .unwrap()
69    }
70
71    pub trait DeterministicInstantiation<Chain: CwEnv>:
72        ContractInstance<Chain> + CwOrchUpload<Chain> + MigratableContract
73    {
74        /// Instantiate blob and migrate to your desired contract.
75        /// It will upload your contract, if it's not uploaded already
76        ///
77        /// Checksum of the uploaded blob_code_id on chain should match [CwBlob::checksum()]
78        fn deterministic_instantiate(
79            &self,
80            migrate_msg: &Self::MigrateMsg,
81            // Ensures blob is uploaded and avoid couple of redundant checks
82            blob_code_id: u64,
83            expected_addr: CanonicalAddr,
84            salt: Binary,
85        ) -> Result<(), CwOrchError> {
86            let chain = self.environment();
87            let on_chain_checksum = chain
88                .wasm_querier()
89                .code_id_hash(blob_code_id)
90                .map_err(Into::into)?;
91            let creator = chain.sender_addr();
92            let label = self.id();
93
94            // Check stored checksum matches
95            {
96                let expected_checksum = checksum();
97                if on_chain_checksum != expected_checksum {
98                    return Err(CwOrchError::StdErr(format!(
99                "Expected blob checksum: {expected_checksum}, stored under given code_id: {on_chain_checksum}"
100            )));
101                }
102            }
103
104            // Check incoming address of instantiated blob
105            {
106                let actual_addr = self.deterministic_address(&salt)?;
107                if actual_addr != expected_addr {
108                    return Err(CwOrchError::StdErr(
109                        "Predicted blob address doesn't match to the expected".to_owned(),
110                    ));
111                }
112            }
113
114            let response = chain
115                .instantiate2(
116                    blob_code_id,
117                    &cosmwasm_std::Empty {},
118                    Some(&label),
119                    Some(&creator),
120                    &[],
121                    salt,
122                )
123                .map_err(Into::into)?;
124            let blob_address = response.instantiated_contract_address()?;
125            let blob_cosmrs_account_id: cosmrs::AccountId = blob_address.as_str().parse().unwrap();
126            if blob_cosmrs_account_id.to_bytes() != expected_addr.as_slice() {
127                // This shouldn't ever happen because we checked instantiate2 address before actually instantiating
128                // But if it have different address then we have bad bug
129                panic!("Unexpected error: Instantiated blob address doesn't match to the expected");
130            }
131
132            self.upload_if_needed()?;
133            let contract_code_id = self.code_id()?;
134            self.set_address(&blob_address);
135            self.migrate(migrate_msg, contract_code_id)?;
136            Ok(())
137        }
138
139        fn deterministic_address(
140            &self,
141            salt: &Binary,
142        ) -> Result<CanonicalAddr, Instantiate2AddressError> {
143            let creator = self.environment().sender_addr();
144            let account_id: cosmrs::AccountId = creator.as_str().parse().unwrap();
145            let canon_creator = CanonicalAddr::from(account_id.to_bytes());
146            let actual_addr =
147                instantiate2_address(checksum().as_slice(), &canon_creator, salt.as_slice())?;
148            Ok(actual_addr)
149        }
150    }
151}
152
153#[cfg(test)]
154mod test {
155    use interface::{checksum, wasm_path};
156
157    use super::*;
158
159    #[test]
160    fn test_checksum() {
161        let checksum = checksum();
162        let expected_checksum = wasm_path().checksum().unwrap();
163        assert_eq!(checksum, expected_checksum);
164    }
165}