cw_multi_test/
wasm.rs

1use crate::addresses::{AddressGenerator, SimpleAddressGenerator};
2use crate::app::{CosmosRouter, RouterQuerier};
3use crate::checksums::{ChecksumGenerator, SimpleChecksumGenerator};
4use crate::contracts::Contract;
5use crate::error::{bail, AnyContext, AnyError, AnyResult, Error};
6use crate::executor::AppResponse;
7use crate::prefixed_storage::typed_prefixed_storage::{
8    StoragePrefix, TypedPrefixedStorage, TypedPrefixedStorageMut,
9};
10use crate::prefixed_storage::{PrefixedStorage, ReadonlyPrefixedStorage};
11use crate::transactions::transactional;
12use cosmwasm_std::testing::mock_wasmd_attr;
13#[cfg(feature = "stargate")]
14use cosmwasm_std::GovMsg;
15#[cfg(feature = "cosmwasm_2_2")]
16use cosmwasm_std::MigrateInfo;
17use cosmwasm_std::{
18    to_json_binary, Addr, Api, Attribute, BankMsg, Binary, BlockInfo, Checksum, Coin, ContractInfo,
19    ContractInfoResponse, CosmosMsg, CustomMsg, CustomQuery, Deps, DepsMut, Env, Event,
20    MessageInfo, MsgResponse, Order, Querier, QuerierWrapper, Record, Reply, ReplyOn, Response,
21    StdResult, Storage, SubMsg, SubMsgResponse, SubMsgResult, TransactionInfo, WasmMsg, WasmQuery,
22};
23#[cfg(feature = "staking")]
24use cosmwasm_std::{DistributionMsg, StakingMsg};
25use cw_storage_plus::Map;
26use prost::Message;
27use schemars::JsonSchema;
28use serde::de::DeserializeOwned;
29use serde::{Deserialize, Serialize};
30use std::borrow::Borrow;
31use std::collections::BTreeMap;
32use std::fmt::Debug;
33
34/// Contract state kept in storage, separate from the contracts themselves (contract code).
35const CONTRACTS: Map<&Addr, ContractData> = Map::new("contracts");
36
37/// Contract [address namespace].
38///
39/// [address namespace]: https://github.com/CosmWasm/wasmd/blob/96e2b91144c9a371683555f3c696f882583cc6a2/x/wasm/types/events.go#L59
40const CONTRACT_ATTR: &str = "_contract_address";
41
42/// A structure representing a privileged message.
43#[derive(Clone, Debug, PartialEq, Eq, JsonSchema)]
44pub struct WasmSudo {
45    /// Address of a contract the privileged action will be sent to.
46    pub contract_addr: Addr,
47    /// Message representing privileged action to be executed by contract `sudo` entry-point.
48    pub message: Binary,
49}
50
51impl WasmSudo {
52    /// Creates a new privileged message for specified contract address and action to be executed.
53    pub fn new<T: Serialize>(contract_addr: &Addr, msg: &T) -> StdResult<WasmSudo> {
54        Ok(WasmSudo {
55            contract_addr: contract_addr.clone(),
56            message: to_json_binary(msg)?,
57        })
58    }
59}
60
61/// Contract data includes information about contract,
62/// equivalent of `ContractInfo` in `wasmd` interface.
63#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
64pub struct ContractData {
65    /// Identifier of stored contract code
66    pub code_id: u64,
67    /// Address of account who initially instantiated the contract
68    pub creator: Addr,
69    /// Optional address of account who can execute migrations
70    pub admin: Option<Addr>,
71    /// Metadata passed while contract instantiation
72    pub label: String,
73    /// Blockchain height in the moment of instantiating the contract
74    pub created: u64,
75}
76
77/// Contract code base data.
78struct CodeData {
79    /// Address of an account that initially stored the contract code.
80    creator: Addr,
81    /// Checksum of the contract's code base.
82    checksum: Checksum,
83    /// Identifier of the _source_ code of the contract stored in wasm keeper.
84    source_id: usize,
85}
86
87/// This trait implements the interface of the Wasm module.
88pub trait Wasm<ExecC, QueryC> {
89    /// Handles all `WasmMsg` messages.
90    fn execute(
91        &self,
92        api: &dyn Api,
93        storage: &mut dyn Storage,
94        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
95        block: &BlockInfo,
96        sender: Addr,
97        msg: WasmMsg,
98    ) -> AnyResult<AppResponse>;
99
100    /// Handles all `WasmQuery` requests.
101    fn query(
102        &self,
103        api: &dyn Api,
104        storage: &dyn Storage,
105        querier: &dyn Querier,
106        block: &BlockInfo,
107        request: WasmQuery,
108    ) -> AnyResult<Binary>;
109
110    /// Handles all sudo messages, this is an admin interface and can not be called via `CosmosMsg`.
111    fn sudo(
112        &self,
113        api: &dyn Api,
114        storage: &mut dyn Storage,
115        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
116        block: &BlockInfo,
117        msg: WasmSudo,
118    ) -> AnyResult<AppResponse>;
119
120    /// Stores the contract's code and returns an identifier of the stored contract's code.
121    fn store_code(&mut self, creator: Addr, code: Box<dyn Contract<ExecC, QueryC>>) -> u64;
122
123    /// Stores the contract's code under specified identifier,
124    /// returns the same code identifier when successful.
125    fn store_code_with_id(
126        &mut self,
127        creator: Addr,
128        code_id: u64,
129        code: Box<dyn Contract<ExecC, QueryC>>,
130    ) -> AnyResult<u64>;
131
132    /// Duplicates the contract's code with specified identifier
133    /// and returns an identifier of the copy of the contract's code.
134    fn duplicate_code(&mut self, code_id: u64) -> AnyResult<u64>;
135
136    /// Returns `ContractData` for the contract with specified address.
137    fn contract_data(&self, storage: &dyn Storage, address: &Addr) -> AnyResult<ContractData>;
138
139    /// Returns a raw state dump of all key-values held by a contract with specified address.
140    fn dump_wasm_raw(&self, storage: &dyn Storage, address: &Addr) -> Vec<Record>;
141
142    /// Returns the namespace of the contract storage.
143    fn contract_namespace(&self, contract: &Addr) -> Vec<u8> {
144        let mut name = b"contract_data/".to_vec();
145        name.extend_from_slice(contract.as_bytes());
146        name
147    }
148
149    /// Returns **read-only** (not mutable) contract storage.
150    fn contract_storage<'a>(
151        &self,
152        storage: &'a dyn Storage,
153        address: &Addr,
154    ) -> Box<dyn Storage + 'a> {
155        // We double-namespace this, once from global storage -> wasm_storage
156        // then from wasm_storage -> the contracts subspace
157        let namespace = self.contract_namespace(address);
158        let storage: TypedPrefixedStorage<'_, WasmKeeper<ExecC, QueryC>> =
159            WasmStorage::multilevel(storage, &namespace);
160        let prefixed_storage: ReadonlyPrefixedStorage = storage.into();
161        Box::new(prefixed_storage)
162    }
163
164    /// Returns **read-write** (mutable) contract storage.
165    fn contract_storage_mut<'a>(
166        &self,
167        storage: &'a mut dyn Storage,
168        address: &Addr,
169    ) -> Box<dyn Storage + 'a> {
170        // We double-namespace this, once from global storage -> wasm_storage
171        // then from wasm_storage -> the contracts subspace
172        let namespace = self.contract_namespace(address);
173        let storage: TypedPrefixedStorageMut<'_, WasmKeeper<ExecC, QueryC>> =
174            WasmStorageMut::multilevel(storage, &namespace);
175        let prefixed_storage: PrefixedStorage = storage.into();
176        Box::new(prefixed_storage)
177    }
178}
179
180/// A structure representing a default wasm keeper.
181pub struct WasmKeeper<ExecC, QueryC> {
182    /// Contract codes that stand for wasm code in real-life blockchain.
183    code_base: Vec<Box<dyn Contract<ExecC, QueryC>>>,
184    /// Code data with code base identifier and additional attributes.
185    code_data: BTreeMap<u64, CodeData>,
186    /// Contract's address generator.
187    address_generator: Box<dyn AddressGenerator>,
188    /// Contract's code checksum generator.
189    checksum_generator: Box<dyn ChecksumGenerator>,
190    /// Just markers to make type elision fork when using it as `Wasm` trait
191    _p: std::marker::PhantomData<QueryC>,
192}
193
194impl<ExecC, QueryC> StoragePrefix for WasmKeeper<ExecC, QueryC> {
195    const NAMESPACE: &'static [u8] = b"wasm";
196}
197type WasmStorage<'a, ExecC, QueryC> = TypedPrefixedStorage<'a, WasmKeeper<ExecC, QueryC>>;
198type WasmStorageMut<'a, ExecC, QueryC> = TypedPrefixedStorageMut<'a, WasmKeeper<ExecC, QueryC>>;
199
200impl<ExecC, QueryC> Default for WasmKeeper<ExecC, QueryC> {
201    /// Returns the default value for [WasmKeeper].
202    fn default() -> Self {
203        Self {
204            code_base: Vec::default(),
205            code_data: BTreeMap::default(),
206            address_generator: Box::new(SimpleAddressGenerator),
207            checksum_generator: Box::new(SimpleChecksumGenerator),
208            _p: std::marker::PhantomData,
209        }
210    }
211}
212
213impl<ExecC, QueryC> Wasm<ExecC, QueryC> for WasmKeeper<ExecC, QueryC>
214where
215    ExecC: CustomMsg + DeserializeOwned + 'static,
216    QueryC: CustomQuery + DeserializeOwned + 'static,
217{
218    fn execute(
219        &self,
220        api: &dyn Api,
221        storage: &mut dyn Storage,
222        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
223        block: &BlockInfo,
224        sender: Addr,
225        msg: WasmMsg,
226    ) -> AnyResult<AppResponse> {
227        self.execute_wasm(api, storage, router, block, sender.clone(), msg.clone())
228            .context(format!(
229                "Error executing WasmMsg:\n  sender: {sender}\n  {msg:?}"
230            ))
231    }
232
233    fn query(
234        &self,
235        api: &dyn Api,
236        storage: &dyn Storage,
237        querier: &dyn Querier,
238        block: &BlockInfo,
239        request: WasmQuery,
240    ) -> AnyResult<Binary> {
241        match request {
242            WasmQuery::Smart { contract_addr, msg } => {
243                let addr = api.addr_validate(&contract_addr)?;
244                self.query_smart(addr, api, storage, querier, block, msg.into())
245            }
246            WasmQuery::Raw { contract_addr, key } => {
247                let addr = api.addr_validate(&contract_addr)?;
248                Ok(self.query_raw(addr, storage, &key))
249            }
250            WasmQuery::ContractInfo { contract_addr } => {
251                let addr = api.addr_validate(&contract_addr)?;
252                let contract = self.contract_data(storage, &addr)?;
253                let res = ContractInfoResponse::new(
254                    contract.code_id,
255                    contract.creator,
256                    contract.admin,
257                    false,
258                    None,
259                );
260                to_json_binary(&res).map_err(Into::into)
261            }
262            #[cfg(feature = "cosmwasm_1_2")]
263            WasmQuery::CodeInfo { code_id } => {
264                let code_data = self.code_data(code_id)?;
265                let res = cosmwasm_std::CodeInfoResponse::new(
266                    code_id,
267                    code_data.creator.clone(),
268                    code_data.checksum,
269                );
270                to_json_binary(&res).map_err(Into::into)
271            }
272            _ => unimplemented!("{}", Error::unsupported_wasm_query(request)),
273        }
274    }
275
276    fn sudo(
277        &self,
278        api: &dyn Api,
279        storage: &mut dyn Storage,
280        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
281        block: &BlockInfo,
282        msg: WasmSudo,
283    ) -> AnyResult<AppResponse> {
284        let custom_event = Event::new("sudo").add_attribute(CONTRACT_ATTR, &msg.contract_addr);
285        let res = self.call_sudo(
286            msg.contract_addr.clone(),
287            api,
288            storage,
289            router,
290            block,
291            msg.message.to_vec(),
292        )?;
293        let (res, msgs) = self.build_app_response(&msg.contract_addr, custom_event, res);
294        self.process_response(api, router, storage, block, msg.contract_addr, res, msgs)
295    }
296
297    /// Stores the contract's code in the in-memory lookup table.
298    /// Returns an identifier of the stored contract code.
299    fn store_code(&mut self, creator: Addr, code: Box<dyn Contract<ExecC, QueryC>>) -> u64 {
300        let code_id = self
301            .next_code_id()
302            .unwrap_or_else(|| panic!("{}", Error::NoMoreCodeIdAvailable));
303        self.save_code(code_id, creator, code)
304    }
305
306    /// Stores the contract's code in the in-memory lookup table.
307    /// Returns an identifier of the stored contract code.
308    fn store_code_with_id(
309        &mut self,
310        creator: Addr,
311        code_id: u64,
312        code: Box<dyn Contract<ExecC, QueryC>>,
313    ) -> AnyResult<u64> {
314        // validate provided contract code identifier
315        if self.code_data.contains_key(&code_id) {
316            bail!(Error::duplicated_code_id(code_id));
317        } else if code_id == 0 {
318            bail!(Error::invalid_code_id());
319        }
320        Ok(self.save_code(code_id, creator, code))
321    }
322
323    /// Duplicates the contract's code with specified identifier.
324    /// Returns an identifier of the copy of the contract's code.
325    fn duplicate_code(&mut self, code_id: u64) -> AnyResult<u64> {
326        let code_data = self.code_data(code_id)?;
327        let new_code_id = self
328            .next_code_id()
329            .ok_or_else(Error::no_more_code_id_available)?;
330        self.code_data.insert(
331            new_code_id,
332            CodeData {
333                creator: code_data.creator.clone(),
334                checksum: code_data.checksum,
335                source_id: code_data.source_id,
336            },
337        );
338        Ok(new_code_id)
339    }
340
341    /// Returns `ContractData` for the contract with specified address.
342    fn contract_data(&self, storage: &dyn Storage, address: &Addr) -> AnyResult<ContractData> {
343        let storage: TypedPrefixedStorage<'_, WasmKeeper<ExecC, QueryC>> =
344            WasmStorage::new(storage);
345        CONTRACTS.load(&storage, address).map_err(Into::into)
346    }
347
348    /// Returns a raw state dump of all key-values held by a contract with specified address.
349    fn dump_wasm_raw(&self, storage: &dyn Storage, address: &Addr) -> Vec<Record> {
350        let storage = self.contract_storage(storage, address);
351        storage.range(None, None, Order::Ascending).collect()
352    }
353}
354
355impl<ExecC, QueryC> WasmKeeper<ExecC, QueryC>
356where
357    ExecC: CustomMsg + DeserializeOwned + 'static,
358    QueryC: CustomQuery + DeserializeOwned + 'static,
359{
360    /// Creates a wasm keeper with default settings.
361    ///
362    /// # Example
363    ///
364    /// ```
365    /// use cw_multi_test::{no_init, AppBuilder, WasmKeeper};
366    ///
367    /// // create wasm keeper
368    /// let wasm_keeper = WasmKeeper::new();
369    ///
370    /// // create and use the application with newly created wasm keeper
371    /// let mut app = AppBuilder::default().with_wasm(wasm_keeper).build(no_init);
372    /// ```
373    pub fn new() -> Self {
374        Self::default()
375    }
376
377    /// Populates an existing [WasmKeeper] with custom contract address generator.
378    ///
379    /// # Example
380    ///
381    /// ```
382    /// use cosmwasm_std::{Addr, Api, Storage};
383    /// use cw_multi_test::{no_init, AddressGenerator, AppBuilder, WasmKeeper};
384    /// use cw_multi_test::error::AnyResult;
385    /// # use cosmwasm_std::testing::MockApi;
386    ///
387    /// struct CustomAddressGenerator;
388    ///
389    /// impl AddressGenerator for CustomAddressGenerator {
390    ///     fn contract_address(
391    ///         &self,
392    ///         api: &dyn Api,
393    ///         storage: &mut dyn Storage,
394    ///         code_id: u64,
395    ///         instance_id: u64,
396    ///     ) -> AnyResult<Addr> {
397    ///         // here implement your address generation logic
398    /// #       Ok(MockApi::default().addr_make("test_address"))
399    ///     }
400    /// }
401    ///
402    /// // populate wasm with your custom address generator
403    /// let wasm_keeper = WasmKeeper::new().with_address_generator(CustomAddressGenerator);
404    ///
405    /// // create and use the application with customized wasm keeper
406    /// let mut app = AppBuilder::default().with_wasm(wasm_keeper).build(no_init);
407    /// ```
408    pub fn with_address_generator(
409        mut self,
410        address_generator: impl AddressGenerator + 'static,
411    ) -> Self {
412        self.address_generator = Box::new(address_generator);
413        self
414    }
415
416    /// Populates an existing [WasmKeeper] with custom checksum generator for the contract code.
417    ///
418    /// # Example
419    ///
420    /// ```
421    /// use cosmwasm_std::{Addr, Checksum};
422    /// use cw_multi_test::{no_init, AppBuilder, ChecksumGenerator, WasmKeeper};
423    ///
424    /// struct MyChecksumGenerator;
425    ///
426    /// impl ChecksumGenerator for MyChecksumGenerator {
427    ///     fn checksum(&self, creator: &Addr, code_id: u64) -> Checksum {
428    ///         // here implement your custom checksum generator
429    /// #       Checksum::from_hex("custom_checksum").unwrap()
430    ///     }
431    /// }
432    ///
433    /// // populate wasm keeper with your custom checksum generator
434    /// let wasm_keeper = WasmKeeper::new().with_checksum_generator(MyChecksumGenerator);
435    ///
436    /// // create and use the application with customized wasm keeper
437    /// let mut app = AppBuilder::default().with_wasm(wasm_keeper).build(no_init);
438    /// ```
439    pub fn with_checksum_generator(
440        mut self,
441        checksum_generator: impl ChecksumGenerator + 'static,
442    ) -> Self {
443        self.checksum_generator = Box::new(checksum_generator);
444        self
445    }
446
447    /// Returns a handler to code of the contract with specified code id.
448    pub fn contract_code(&self, code_id: u64) -> AnyResult<&dyn Contract<ExecC, QueryC>> {
449        let code_data = self.code_data(code_id)?;
450        Ok(self.code_base[code_data.source_id].borrow())
451    }
452
453    /// Returns code data of the contract with specified code id.
454    fn code_data(&self, code_id: u64) -> AnyResult<&CodeData> {
455        if code_id < 1 {
456            bail!(Error::invalid_code_id());
457        }
458        Ok(self
459            .code_data
460            .get(&code_id)
461            .ok_or_else(|| Error::unregistered_code_id(code_id))?)
462    }
463
464    /// Validates all attributes.
465    ///
466    /// In `wasmd`, before version v0.45.0 empty attribute values were not allowed.
467    /// Since `wasmd` v0.45.0 empty attribute values are allowed,
468    /// so the value is not validated anymore.
469    fn verify_attributes(attributes: &[Attribute]) -> AnyResult<()> {
470        for attr in attributes {
471            let key = attr.key.trim();
472            let val = attr.value.trim();
473            if key.is_empty() {
474                bail!(Error::empty_attribute_key(val));
475            }
476            if key.starts_with('_') {
477                bail!(Error::reserved_attribute_key(key));
478            }
479        }
480        Ok(())
481    }
482
483    fn verify_response<T>(response: Response<T>) -> AnyResult<Response<T>>
484    where
485        T: CustomMsg,
486    {
487        Self::verify_attributes(&response.attributes)?;
488
489        for event in &response.events {
490            Self::verify_attributes(&event.attributes)?;
491            let ty = event.ty.trim();
492            if ty.len() < 2 {
493                bail!(Error::event_type_too_short(ty));
494            }
495        }
496
497        Ok(response)
498    }
499
500    fn save_code(
501        &mut self,
502        code_id: u64,
503        creator: Addr,
504        code: Box<dyn Contract<ExecC, QueryC>>,
505    ) -> u64 {
506        // prepare the next identifier for the contract's code
507        let source_id = self.code_base.len();
508        // prepare the contract's Wasm blob checksum
509        let checksum = code
510            .checksum()
511            .unwrap_or(self.checksum_generator.checksum(&creator, code_id));
512        // store the 'source' code of the contract
513        self.code_base.push(code);
514        // store the additional code attributes like creator address and checksum
515        self.code_data.insert(
516            code_id,
517            CodeData {
518                creator,
519                checksum,
520                source_id,
521            },
522        );
523        code_id
524    }
525
526    /// Returns the next contract's code identifier.
527    fn next_code_id(&self) -> Option<u64> {
528        self.code_data.keys().last().unwrap_or(&0u64).checked_add(1)
529    }
530
531    /// Executes the contract's `query` entry-point.
532    pub fn query_smart(
533        &self,
534        address: Addr,
535        api: &dyn Api,
536        storage: &dyn Storage,
537        querier: &dyn Querier,
538        block: &BlockInfo,
539        msg: Vec<u8>,
540    ) -> AnyResult<Binary> {
541        self.with_storage_readonly(
542            api,
543            storage,
544            querier,
545            block,
546            address,
547            |handler, deps, env| handler.query(deps, env, msg),
548        )
549    }
550
551    /// Returns the value stored under specified key in contracts storage.
552    pub fn query_raw(&self, address: Addr, storage: &dyn Storage, key: &[u8]) -> Binary {
553        let storage = self.contract_storage(storage, &address);
554        let data = storage.get(key).unwrap_or_default();
555        data.into()
556    }
557
558    fn send<T>(
559        &self,
560        api: &dyn Api,
561        storage: &mut dyn Storage,
562        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
563        block: &BlockInfo,
564        sender: T,
565        recipient: String,
566        amount: &[Coin],
567    ) -> AnyResult<AppResponse>
568    where
569        T: Into<Addr>,
570    {
571        if !amount.is_empty() {
572            let msg: CosmosMsg<ExecC> = BankMsg::Send {
573                to_address: recipient,
574                amount: amount.to_vec(),
575            }
576            .into();
577            let res = router.execute(api, storage, block, sender.into(), msg)?;
578            Ok(res)
579        } else {
580            Ok(AppResponse::default())
581        }
582    }
583
584    /// unified logic for UpdateAdmin and ClearAdmin messages
585    fn update_admin(
586        &self,
587        api: &dyn Api,
588        storage: &mut dyn Storage,
589        sender: Addr,
590        contract_addr: &str,
591        new_admin: Option<String>,
592    ) -> AnyResult<AppResponse> {
593        let contract_addr = api.addr_validate(contract_addr)?;
594        let admin = new_admin.map(|a| api.addr_validate(&a)).transpose()?;
595
596        // check admin status
597        let mut contract_data = self.contract_data(storage, &contract_addr)?;
598        if contract_data.admin != Some(sender) {
599            bail!(
600                "Only admin can update the contract admin: {:?}",
601                contract_data.admin
602            );
603        }
604        // update admin field
605        contract_data.admin = admin;
606        self.save_contract(storage, &contract_addr, &contract_data)?;
607
608        // No custom events or data here.
609        Ok(AppResponse::default())
610    }
611
612    // this returns the contract address as well, so we can properly resend the data
613    fn execute_wasm(
614        &self,
615        api: &dyn Api,
616        storage: &mut dyn Storage,
617        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
618        block: &BlockInfo,
619        sender: Addr,
620        msg: WasmMsg,
621    ) -> AnyResult<AppResponse> {
622        match msg {
623            WasmMsg::Execute {
624                contract_addr,
625                msg,
626                funds,
627            } => {
628                let contract_addr = api.addr_validate(&contract_addr)?;
629                // first move the cash
630                self.send(
631                    api,
632                    storage,
633                    router,
634                    block,
635                    sender.clone(),
636                    contract_addr.clone().into(),
637                    &funds,
638                )?;
639
640                // then call the contract
641                let info = MessageInfo { sender, funds };
642                let response = self.call_execute(
643                    api,
644                    storage,
645                    contract_addr.clone(),
646                    router,
647                    block,
648                    info,
649                    msg.to_vec(),
650                )?;
651
652                let custom_event =
653                    Event::new("execute").add_attribute(CONTRACT_ATTR, &contract_addr);
654
655                let (sub_response, sub_messages) =
656                    self.build_app_response(&contract_addr, custom_event, response);
657
658                let mut app_response = self.process_response(
659                    api,
660                    router,
661                    storage,
662                    block,
663                    contract_addr,
664                    sub_response,
665                    sub_messages,
666                )?;
667                app_response.data = encode_response_data(app_response.data);
668                Ok(app_response)
669            }
670            WasmMsg::Instantiate {
671                admin,
672                code_id,
673                msg,
674                funds,
675                label,
676            } => self.process_wasm_msg_instantiate(
677                api, storage, router, block, sender, admin, code_id, msg, funds, label, None,
678            ),
679            #[cfg(feature = "cosmwasm_1_2")]
680            WasmMsg::Instantiate2 {
681                admin,
682                code_id,
683                msg,
684                funds,
685                label,
686                salt,
687            } => self.process_wasm_msg_instantiate(
688                api,
689                storage,
690                router,
691                block,
692                sender,
693                admin,
694                code_id,
695                msg,
696                funds,
697                label,
698                Some(salt),
699            ),
700            WasmMsg::Migrate {
701                contract_addr,
702                new_code_id,
703                msg,
704            } => {
705                let contract_addr = api.addr_validate(&contract_addr)?;
706
707                // check admin status and update the stored code_id
708                if new_code_id as usize > self.code_data.len() {
709                    bail!("Cannot migrate contract to unregistered code id");
710                }
711                let mut data = self.contract_data(storage, &contract_addr)?;
712                if data.admin != Some(sender.clone()) {
713                    bail!("Only admin can migrate contract: {:?}", data.admin);
714                }
715                // Save the current (old) code_id for later use.
716                #[cfg(feature = "cosmwasm_2_2")]
717                let old_migrate_version = Some(data.code_id);
718                //  Update the stored code_id.
719                data.code_id = new_code_id;
720                self.save_contract(storage, &contract_addr, &data)?;
721
722                // Then call migrate (the classic version without MigrateInfo).
723                #[cfg(not(feature = "cosmwasm_2_2"))]
724                let res = self.call_migrate(
725                    contract_addr.clone(),
726                    api,
727                    storage,
728                    router,
729                    block,
730                    msg.to_vec(),
731                )?;
732
733                // Then call migrate with MigrateInfo.
734                #[cfg(feature = "cosmwasm_2_2")]
735                let res = self.call_migrate(
736                    contract_addr.clone(),
737                    api,
738                    storage,
739                    router,
740                    block,
741                    msg.to_vec(),
742                    MigrateInfo {
743                        sender,
744                        old_migrate_version,
745                    },
746                )?;
747
748                let custom_event = Event::new("migrate")
749                    .add_attribute(CONTRACT_ATTR, &contract_addr)
750                    .add_attribute("code_id", new_code_id.to_string());
751                let (res, msgs) = self.build_app_response(&contract_addr, custom_event, res);
752                let mut res =
753                    self.process_response(api, router, storage, block, contract_addr, res, msgs)?;
754                res.data = encode_response_data(res.data);
755                Ok(res)
756            }
757            WasmMsg::UpdateAdmin {
758                contract_addr,
759                admin,
760            } => self.update_admin(api, storage, sender, &contract_addr, Some(admin)),
761            WasmMsg::ClearAdmin { contract_addr } => {
762                self.update_admin(api, storage, sender, &contract_addr, None)
763            }
764            _ => unimplemented!("{}", Error::unsupported_wasm_message(msg)),
765        }
766    }
767
768    /// Processes WasmMsg::Instantiate and WasmMsg::Instantiate2 messages.
769    fn process_wasm_msg_instantiate(
770        &self,
771        api: &dyn Api,
772        storage: &mut dyn Storage,
773        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
774        block: &BlockInfo,
775        sender: Addr,
776        admin: Option<String>,
777        code_id: u64,
778        msg: Binary,
779        funds: Vec<Coin>,
780        label: String,
781        salt: Option<Binary>,
782    ) -> AnyResult<AppResponse> {
783        if label.is_empty() {
784            bail!("Label is required on all contracts");
785        }
786
787        let contract_addr = self.register_contract(
788            api,
789            storage,
790            code_id,
791            sender.clone(),
792            admin.map(Addr::unchecked),
793            label,
794            block.height,
795            salt,
796        )?;
797
798        // move the cash
799        self.send(
800            api,
801            storage,
802            router,
803            block,
804            sender.clone(),
805            contract_addr.clone().into(),
806            &funds,
807        )?;
808
809        // then call the contract
810        let info = MessageInfo { sender, funds };
811        let res = self.call_instantiate(
812            contract_addr.clone(),
813            api,
814            storage,
815            router,
816            block,
817            info,
818            msg.to_vec(),
819        )?;
820
821        let custom_event = Event::new("instantiate")
822            .add_attribute(CONTRACT_ATTR, &contract_addr)
823            .add_attribute("code_id", code_id.to_string());
824
825        let (res, msgs) = self.build_app_response(&contract_addr, custom_event, res);
826        let mut res = self.process_response(
827            api,
828            router,
829            storage,
830            block,
831            contract_addr.clone(),
832            res,
833            msgs,
834        )?;
835        res.data = Some(instantiate_response(res.data, &contract_addr));
836        Ok(res)
837    }
838
839    /// This will execute the given messages, making all changes to the local cache.
840    /// This *will* write some data to the cache if the message fails half-way through.
841    /// All sequential calls to RouterCache will be one atomic unit (all commit or all fail).
842    ///
843    /// For normal use cases, you can use Router::execute() or Router::execute_multi().
844    /// This is designed to be handled internally as part of larger process flows.
845    ///
846    /// The `data` on `AppResponse` is data returned from `reply` call, not from execution of
847    /// sub-message itself. In case if `reply` is not called, no `data` is set.
848    fn execute_submsg(
849        &self,
850        api: &dyn Api,
851        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
852        storage: &mut dyn Storage,
853        block: &BlockInfo,
854        contract: Addr,
855        msg: SubMsg<ExecC>,
856    ) -> AnyResult<AppResponse> {
857        let SubMsg {
858            msg,
859            id,
860            reply_on,
861            payload,
862            ..
863        } = msg;
864        // Get the response message type URL, will be needed when calling `reply` entrypoint.
865        let opt_type_url = Self::response_type_url(&msg);
866
867        // Execute the submessage in cache
868        let sub_message_result = transactional(storage, |write_cache, _| {
869            router.execute(api, write_cache, block, contract.clone(), msg)
870        });
871
872        // call reply if meaningful
873        if let Ok(mut r) = sub_message_result {
874            if matches!(reply_on, ReplyOn::Always | ReplyOn::Success) {
875                let mut msg_responses = r.msg_responses.clone();
876                if let Some(type_url_ref) = opt_type_url {
877                    if msg_responses.is_empty() {
878                        msg_responses.push(MsgResponse {
879                            type_url: type_url_ref.to_string(),
880                            value: r.data.clone().unwrap_or_default(),
881                        })
882                    }
883                }
884                let reply = Reply {
885                    id,
886                    payload,
887                    gas_used: 0,
888                    result: SubMsgResult::Ok(
889                        #[allow(deprecated)]
890                        SubMsgResponse {
891                            events: r.events.clone(),
892                            data: r.data.clone(),
893                            msg_responses,
894                        },
895                    ),
896                };
897                // do reply and combine it with the original response
898                let reply_res = self.reply(api, router, storage, block, contract, reply)?;
899                // override data
900                r.data = reply_res.data;
901                // append the events
902                r.events.extend_from_slice(&reply_res.events);
903            } else {
904                // reply is not called, no data should be returned
905                r.data = None;
906            }
907            Ok(r)
908        } else if let Err(e) = sub_message_result {
909            if matches!(reply_on, ReplyOn::Always | ReplyOn::Error) {
910                let reply = Reply {
911                    id,
912                    payload,
913                    gas_used: 0,
914                    result: SubMsgResult::Err(format!("{e:?}")),
915                };
916                self.reply(api, router, storage, block, contract, reply)
917            } else {
918                Err(e)
919            }
920        } else {
921            sub_message_result
922        }
923    }
924
925    fn reply(
926        &self,
927        api: &dyn Api,
928        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
929        storage: &mut dyn Storage,
930        block: &BlockInfo,
931        contract: Addr,
932        reply: Reply,
933    ) -> AnyResult<AppResponse> {
934        let ok_attr = if reply.result.is_ok() {
935            "handle_success"
936        } else {
937            "handle_failure"
938        };
939        let custom_event = Event::new("reply")
940            .add_attribute(CONTRACT_ATTR, &contract)
941            .add_attribute("mode", ok_attr);
942
943        let res = self.call_reply(contract.clone(), api, storage, router, block, reply)?;
944        let (res, msgs) = self.build_app_response(&contract, custom_event, res);
945        self.process_response(api, router, storage, block, contract, res, msgs)
946    }
947
948    /// Captures all the events, data and sub messages from the contract call.
949    ///
950    /// This function does not handle the messages.
951    fn build_app_response(
952        &self,
953        contract: &Addr,
954        custom_event: Event, // entry-point specific custom event added by x/wasm
955        response: Response<ExecC>,
956    ) -> (AppResponse, Vec<SubMsg<ExecC>>) {
957        let Response {
958            messages,
959            attributes,
960            events,
961            data,
962            ..
963        } = response;
964
965        // always add custom event
966        let mut app_events = Vec::with_capacity(2 + events.len());
967        app_events.push(custom_event);
968
969        // we only emit the `wasm` event if some attributes are specified
970        if !attributes.is_empty() {
971            // turn attributes into event and place it first
972            let wasm_event = Event::new("wasm")
973                .add_attribute(CONTRACT_ATTR, contract)
974                .add_attributes(attributes);
975            app_events.push(wasm_event);
976        }
977
978        // These need to get `wasm-` prefix to match the wasmd semantics (custom wasm messages cannot
979        // fake system level event types, like transfer from the bank module)
980        let wasm_events = events.into_iter().map(|mut ev| {
981            ev.ty = format!("wasm-{}", ev.ty);
982            ev.attributes
983                .insert(0, mock_wasmd_attr(CONTRACT_ATTR, contract));
984            ev
985        });
986        app_events.extend(wasm_events);
987
988        let app_response = AppResponse {
989            events: app_events,
990            data,
991            msg_responses: vec![],
992        };
993        (app_response, messages)
994    }
995
996    fn process_response(
997        &self,
998        api: &dyn Api,
999        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
1000        storage: &mut dyn Storage,
1001        block: &BlockInfo,
1002        contract: Addr,
1003        response: AppResponse,
1004        sub_messages: Vec<SubMsg<ExecC>>,
1005    ) -> AnyResult<AppResponse> {
1006        // Unpack the provided response.
1007        let AppResponse {
1008            mut events, data, ..
1009        } = response;
1010        // Recurse in all submessages.
1011        let data = sub_messages
1012            .into_iter()
1013            .try_fold(data, |data, sub_message| {
1014                // Execute the submessage.
1015                let sub_response = self.execute_submsg(
1016                    api,
1017                    router,
1018                    storage,
1019                    block,
1020                    contract.clone(),
1021                    sub_message,
1022                )?;
1023                // COLLECT and append all events from the processed submessage.
1024                events.extend_from_slice(&sub_response.events);
1025                // REPLACE the data with value from the processes submessage (if not empty).
1026                Ok::<_, AnyError>(sub_response.data.or(data))
1027            })?;
1028        // Return the response with updated data, events and message responses taken from
1029        // all processed sub messages. Note that events and message responses are collected,
1030        // but the data is replaced with the data from the last processes submessage.
1031        Ok(AppResponse {
1032            events,
1033            data,
1034            msg_responses: vec![],
1035        })
1036    }
1037
1038    /// Creates a contract address and empty storage instance.
1039    /// Returns the new contract address.
1040    ///
1041    /// You have to call init after this to set up the contract properly.
1042    /// These two steps are separated to have cleaner return values.
1043    pub fn register_contract(
1044        &self,
1045        api: &dyn Api,
1046        storage: &mut dyn Storage,
1047        code_id: u64,
1048        creator: Addr,
1049        admin: impl Into<Option<Addr>>,
1050        label: String,
1051        created: u64,
1052        salt: impl Into<Option<Binary>>,
1053    ) -> AnyResult<Addr> {
1054        // check if the contract's code with specified code_id exists
1055        if code_id as usize > self.code_data.len() {
1056            bail!("Cannot init contract with unregistered code id");
1057        }
1058
1059        // generate a new contract address
1060        let instance_id = self.instance_count(storage) as u64;
1061        let addr = if let Some(salt_binary) = salt.into() {
1062            // generate predictable contract address when salt is provided
1063            let code_data = self.code_data(code_id)?;
1064            let canonical_addr = &api.addr_canonicalize(creator.as_ref())?;
1065            self.address_generator.predictable_contract_address(
1066                api,
1067                storage,
1068                code_id,
1069                instance_id,
1070                code_data.checksum.as_slice(),
1071                canonical_addr,
1072                salt_binary.as_slice(),
1073            )?
1074        } else {
1075            // generate non-predictable contract address
1076            self.address_generator
1077                .contract_address(api, storage, code_id, instance_id)?
1078        };
1079
1080        // contract with the same address must not already exist
1081        if self.contract_data(storage, &addr).is_ok() {
1082            bail!(Error::duplicated_contract_address(addr));
1083        }
1084
1085        // prepare contract data and save new contract instance
1086        let info = ContractData {
1087            code_id,
1088            creator,
1089            admin: admin.into(),
1090            label,
1091            created,
1092        };
1093        self.save_contract(storage, &addr, &info)?;
1094        Ok(addr)
1095    }
1096
1097    /// Executes contract's `execute` entry-point.
1098    pub fn call_execute(
1099        &self,
1100        api: &dyn Api,
1101        storage: &mut dyn Storage,
1102        address: Addr,
1103        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
1104        block: &BlockInfo,
1105        info: MessageInfo,
1106        msg: Vec<u8>,
1107    ) -> AnyResult<Response<ExecC>> {
1108        Self::verify_response(self.with_storage(
1109            api,
1110            storage,
1111            router,
1112            block,
1113            address,
1114            |contract, deps, env| contract.execute(deps, env, info, msg),
1115        )?)
1116    }
1117
1118    /// Executes contract's `instantiate` entry-point.
1119    pub fn call_instantiate(
1120        &self,
1121        address: Addr,
1122        api: &dyn Api,
1123        storage: &mut dyn Storage,
1124        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
1125        block: &BlockInfo,
1126        info: MessageInfo,
1127        msg: Vec<u8>,
1128    ) -> AnyResult<Response<ExecC>> {
1129        Self::verify_response(self.with_storage(
1130            api,
1131            storage,
1132            router,
1133            block,
1134            address,
1135            |contract, deps, env| contract.instantiate(deps, env, info, msg),
1136        )?)
1137    }
1138
1139    /// Executes contract's `reply` entry-point.
1140    pub fn call_reply(
1141        &self,
1142        address: Addr,
1143        api: &dyn Api,
1144        storage: &mut dyn Storage,
1145        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
1146        block: &BlockInfo,
1147        reply: Reply,
1148    ) -> AnyResult<Response<ExecC>> {
1149        Self::verify_response(self.with_storage(
1150            api,
1151            storage,
1152            router,
1153            block,
1154            address,
1155            |contract, deps, env| contract.reply(deps, env, reply),
1156        )?)
1157    }
1158
1159    /// Executes contract's `sudo` entry-point.
1160    pub fn call_sudo(
1161        &self,
1162        address: Addr,
1163        api: &dyn Api,
1164        storage: &mut dyn Storage,
1165        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
1166        block: &BlockInfo,
1167        msg: Vec<u8>,
1168    ) -> AnyResult<Response<ExecC>> {
1169        Self::verify_response(self.with_storage(
1170            api,
1171            storage,
1172            router,
1173            block,
1174            address,
1175            |contract, deps, env| contract.sudo(deps, env, msg),
1176        )?)
1177    }
1178
1179    /// Executes contract's `migrate` entry-point.
1180    #[cfg(not(feature = "cosmwasm_2_2"))]
1181    pub fn call_migrate(
1182        &self,
1183        address: Addr,
1184        api: &dyn Api,
1185        storage: &mut dyn Storage,
1186        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
1187        block: &BlockInfo,
1188        msg: Vec<u8>,
1189    ) -> AnyResult<Response<ExecC>> {
1190        Self::verify_response(self.with_storage(
1191            api,
1192            storage,
1193            router,
1194            block,
1195            address,
1196            |contract, deps, env| contract.migrate(deps, env, msg),
1197        )?)
1198    }
1199
1200    /// Executes contract's `migrate` entry-point.
1201    #[cfg(feature = "cosmwasm_2_2")]
1202    pub fn call_migrate(
1203        &self,
1204        address: Addr,
1205        api: &dyn Api,
1206        storage: &mut dyn Storage,
1207        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
1208        block: &BlockInfo,
1209        msg: Vec<u8>,
1210        info: MigrateInfo,
1211    ) -> AnyResult<Response<ExecC>> {
1212        Self::verify_response(self.with_storage(
1213            api,
1214            storage,
1215            router,
1216            block,
1217            address,
1218            |contract, deps, env| contract.migrate(deps, env, msg, info),
1219        )?)
1220    }
1221
1222    fn get_env<T: Into<Addr>>(&self, address: T, block: &BlockInfo) -> Env {
1223        Env {
1224            block: block.clone(),
1225            contract: ContractInfo {
1226                address: address.into(),
1227            },
1228            transaction: Some(TransactionInfo { index: 0 }),
1229        }
1230    }
1231
1232    fn with_storage_readonly<F, T>(
1233        &self,
1234        api: &dyn Api,
1235        storage: &dyn Storage,
1236        querier: &dyn Querier,
1237        block: &BlockInfo,
1238        address: Addr,
1239        action: F,
1240    ) -> AnyResult<T>
1241    where
1242        F: FnOnce(&dyn Contract<ExecC, QueryC>, Deps<QueryC>, Env) -> AnyResult<T>,
1243    {
1244        let contract = self.contract_data(storage, &address)?;
1245        let handler = self.contract_code(contract.code_id)?;
1246        let storage = self.contract_storage(storage, &address);
1247        let env = self.get_env(address, block);
1248
1249        let deps = Deps {
1250            storage: storage.as_ref(),
1251            api,
1252            querier: QuerierWrapper::new(querier),
1253        };
1254        action(handler, deps, env)
1255    }
1256
1257    fn with_storage<F, T>(
1258        &self,
1259        api: &dyn Api,
1260        storage: &mut dyn Storage,
1261        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
1262        block: &BlockInfo,
1263        address: Addr,
1264        action: F,
1265    ) -> AnyResult<T>
1266    where
1267        F: FnOnce(&dyn Contract<ExecC, QueryC>, DepsMut<QueryC>, Env) -> AnyResult<T>,
1268        ExecC: DeserializeOwned,
1269    {
1270        let contract = self.contract_data(storage, &address)?;
1271        let handler = self.contract_code(contract.code_id)?;
1272
1273        // We don't actually need a transaction here, as it is already embedded in a transactional.
1274        // execute_submsg or App.execute_multi.
1275        // However, we need to get write and read access to the same storage in two different objects,
1276        // and this is the only way I know how to do so.
1277        transactional(storage, |write_cache, read_store| {
1278            let mut contract_storage = self.contract_storage_mut(write_cache, &address);
1279            let querier = RouterQuerier::new(router, api, read_store, block);
1280            let env = self.get_env(address, block);
1281
1282            let deps = DepsMut {
1283                storage: contract_storage.as_mut(),
1284                api,
1285                querier: QuerierWrapper::new(&querier),
1286            };
1287            action(handler, deps, env)
1288        })
1289    }
1290
1291    /// Saves contract data in a storage under specified address.
1292    pub fn save_contract(
1293        &self,
1294        storage: &mut dyn Storage,
1295        address: &Addr,
1296        contract: &ContractData,
1297    ) -> AnyResult<()> {
1298        let mut storage: TypedPrefixedStorageMut<'_, WasmKeeper<ExecC, QueryC>> =
1299            WasmStorageMut::new(storage);
1300        CONTRACTS
1301            .save(&mut storage, address, contract)
1302            .map_err(Into::into)
1303    }
1304
1305    /// Returns the number of all contract instances.
1306    fn instance_count(&self, storage: &dyn Storage) -> usize {
1307        let storage: TypedPrefixedStorage<'_, WasmKeeper<ExecC, QueryC>> =
1308            WasmStorage::new(storage);
1309        CONTRACTS
1310            .range_raw(&storage, None, None, Order::Ascending)
1311            .count()
1312    }
1313
1314    /// Returns the response type for specified message.
1315    #[rustfmt::skip]
1316    fn response_type_url(msg: &CosmosMsg<ExecC>) -> Option<&'static str> {
1317        #[allow(clippy::collapsible_match)]
1318        match &msg {
1319            CosmosMsg::Bank(bank_msg) => match bank_msg {
1320                BankMsg::Send { .. } => Some("/cosmos.bank.v1beta1.MsgSendResponse"),
1321                BankMsg::Burn { .. } => Some("/cosmos.bank.v1beta1.MsgBurnResponse"),
1322                _ => None,
1323            },
1324            CosmosMsg::Custom(..) => None,
1325            #[cfg(feature = "staking")]
1326            CosmosMsg::Staking(staking_msg) => match staking_msg {
1327                StakingMsg::Delegate { .. } => Some("/cosmos.staking.v1beta1.MsgDelegateResponse"),
1328                StakingMsg::Undelegate { .. } => Some("/cosmos.staking.v1beta1.MsgUndelegateResponse"),
1329                StakingMsg::Redelegate { .. } => Some("/cosmos.staking.v1beta1.MsgBeginRedelegateResponse"),
1330                _ => None,
1331            },
1332            #[cfg(feature = "staking")]
1333            CosmosMsg::Distribution(distribution_msg) => match distribution_msg {
1334                #[cfg(feature = "cosmwasm_1_3")]
1335                DistributionMsg::FundCommunityPool { .. } => Some("/cosmos.distribution.v1beta1.MsgFundCommunityPoolResponse"),
1336                DistributionMsg::SetWithdrawAddress { .. } => Some("/cosmos.distribution.v1beta1.MsgSetWithdrawAddressResponse"),
1337                DistributionMsg::WithdrawDelegatorReward { .. } => Some("/cosmos.distribution.v1beta1.MsgWithdrawDelegatorRewardResponse"),
1338                _ => None,
1339            },
1340            #[cfg(feature = "stargate")]
1341            #[allow(deprecated)]
1342            CosmosMsg::Stargate { .. } => None,
1343            #[cfg(feature = "cosmwasm_2_0")]
1344            CosmosMsg::Any(..) => None,
1345            #[cfg(feature = "stargate")]
1346            CosmosMsg::Ibc(..) => None,
1347            CosmosMsg::Wasm(wasm_msg) => match wasm_msg {
1348                WasmMsg::Instantiate { .. } => Some("/cosmwasm.wasm.v1.MsgInstantiateContractResponse"),
1349                #[cfg(feature = "cosmwasm_1_2")]
1350                WasmMsg::Instantiate2 { .. } => Some("/cosmwasm.wasm.v1.MsgInstantiateContract2Response"),
1351                WasmMsg::Execute { .. } => Some("/cosmwasm.wasm.v1.MsgExecuteContractResponse"),
1352                WasmMsg::Migrate { .. } => Some("/cosmwasm.wasm.v1.MsgMigrateContractResponse"),
1353                WasmMsg::UpdateAdmin { .. } => Some("/cosmwasm.wasm.v1.MsgUpdateAdminResponse"),
1354                WasmMsg::ClearAdmin { .. } => Some("/cosmwasm.wasm.v1.MsgClearAdminResponse"),
1355                _ => None,
1356            },
1357            #[cfg(feature = "stargate")]
1358            CosmosMsg::Gov(gov_msg) => match gov_msg {
1359                GovMsg::Vote { .. } => Some("/cosmos.gov.v1beta1.MsgVoteResponse"),
1360                #[cfg(feature = "cosmwasm_1_2")]
1361                GovMsg::VoteWeighted { .. } => Some("/cosmos.gov.v1beta1.MsgVoteWeightedResponse"),
1362            },
1363            _ => None,
1364        }
1365    }
1366}
1367
1368#[derive(Clone, PartialEq, Message)]
1369struct InstantiateResponse {
1370    #[prost(string, tag = "1")]
1371    pub address: String,
1372    #[prost(bytes, tag = "2")]
1373    pub data: Vec<u8>,
1374}
1375
1376fn instantiate_response(data: Option<Binary>, contact_address: &Addr) -> Binary {
1377    let data = data.unwrap_or_default().to_vec();
1378    let init_data = InstantiateResponse {
1379        address: contact_address.into(),
1380        data,
1381    };
1382    let mut new_data = Vec::<u8>::with_capacity(init_data.encoded_len());
1383    // the data must encode successfully
1384    init_data.encode(&mut new_data).unwrap();
1385    new_data.into()
1386}
1387
1388#[derive(Clone, PartialEq, Message)]
1389struct ExecuteResponse {
1390    #[prost(bytes, tag = "1")]
1391    pub data: Vec<u8>,
1392}
1393
1394/// Encodes the response data.
1395fn encode_response_data(data: Option<Binary>) -> Option<Binary> {
1396    data.map(|d| {
1397        let execute_response = ExecuteResponse { data: d.to_vec() };
1398        let mut encoded_data = Vec::<u8>::with_capacity(execute_response.encoded_len());
1399        execute_response.encode(&mut encoded_data).unwrap();
1400        encoded_data.into()
1401    })
1402}
1403
1404#[cfg(test)]
1405mod test {
1406    use super::*;
1407    use crate::app::Router;
1408    use crate::bank::BankKeeper;
1409    use crate::featured::staking::{DistributionKeeper, StakeKeeper};
1410    use crate::module::FailingModule;
1411    use crate::test_helpers::{caller, error, payout};
1412    use crate::transactions::StorageTransaction;
1413    use crate::{GovFailingModule, IbcFailingModule, StargateFailing};
1414    use cosmwasm_std::testing::{message_info, mock_env, MockApi, MockQuerier, MockStorage};
1415    #[cfg(feature = "cosmwasm_1_2")]
1416    use cosmwasm_std::CodeInfoResponse;
1417    use cosmwasm_std::{
1418        coin, from_json, to_json_vec, CanonicalAddr, CosmosMsg, Empty, HexBinary, StdError,
1419    };
1420    use std::slice;
1421
1422    /// Type alias for default build `Router` to make its reference in typical scenario
1423    type BasicRouter<ExecC = Empty, QueryC = Empty> = Router<
1424        BankKeeper,
1425        FailingModule<ExecC, QueryC, Empty>,
1426        WasmKeeper<ExecC, QueryC>,
1427        StakeKeeper,
1428        DistributionKeeper,
1429        IbcFailingModule,
1430        GovFailingModule,
1431        StargateFailing,
1432    >;
1433
1434    fn wasm_keeper() -> WasmKeeper<Empty, Empty> {
1435        WasmKeeper::new()
1436    }
1437
1438    fn mock_router() -> BasicRouter {
1439        Router {
1440            wasm: WasmKeeper::new(),
1441            bank: BankKeeper::new(),
1442            custom: FailingModule::new(),
1443            staking: StakeKeeper::new(),
1444            distribution: DistributionKeeper::new(),
1445            ibc: IbcFailingModule::new(),
1446            gov: GovFailingModule::new(),
1447            stargate: StargateFailing,
1448        }
1449    }
1450
1451    #[test]
1452    fn register_contract() {
1453        let api = MockApi::default();
1454
1455        // prepare user addresses
1456        let creator_addr = api.addr_make("creator");
1457        let user_addr = api.addr_make("foobar");
1458        let admin_addr = api.addr_make("admin");
1459        let unregistered_addr = api.addr_make("unregistered");
1460
1461        let mut wasm_storage = MockStorage::new();
1462        let mut wasm_keeper = wasm_keeper();
1463        let block = mock_env().block;
1464        let code_id = wasm_keeper.store_code(creator_addr, error::contract(false));
1465
1466        transactional(&mut wasm_storage, |cache, _| {
1467            // cannot register contract with unregistered codeId
1468            wasm_keeper.register_contract(
1469                &api,
1470                cache,
1471                code_id + 1,
1472                user_addr.clone(),
1473                admin_addr.clone(),
1474                "label".to_owned(),
1475                1000,
1476                None,
1477            )
1478        })
1479        .unwrap_err();
1480
1481        let contract_addr = transactional(&mut wasm_storage, |cache, _| {
1482            // we can register a new instance of this code
1483            wasm_keeper.register_contract(
1484                &api,
1485                cache,
1486                code_id,
1487                user_addr.clone(),
1488                admin_addr.clone(),
1489                "label".to_owned(),
1490                1000,
1491                None,
1492            )
1493        })
1494        .unwrap();
1495
1496        // verify contract data are as expected
1497        let contract_data = wasm_keeper
1498            .contract_data(&wasm_storage, &contract_addr)
1499            .unwrap();
1500
1501        assert_eq!(
1502            contract_data,
1503            ContractData {
1504                code_id,
1505                creator: user_addr.clone(),
1506                admin: admin_addr.into(),
1507                label: "label".to_owned(),
1508                created: 1000,
1509            }
1510        );
1511
1512        let err = transactional(&mut wasm_storage, |cache, _| {
1513            // now, we call this contract and see the error message from the contract
1514            let info = message_info(&user_addr, &[]);
1515            wasm_keeper.call_instantiate(
1516                contract_addr.clone(),
1517                &api,
1518                cache,
1519                &mock_router(),
1520                &block,
1521                info,
1522                b"{}".to_vec(),
1523            )
1524        })
1525        .unwrap_err();
1526
1527        // StdError from contract_error auto-converted to string
1528        assert_eq!(
1529            StdError::generic_err("Init failed"),
1530            err.downcast().unwrap()
1531        );
1532
1533        let err = transactional(&mut wasm_storage, |cache, _| {
1534            // and the error for calling an unregistered contract
1535            let info = message_info(&user_addr, &[]);
1536            wasm_keeper.call_instantiate(
1537                unregistered_addr,
1538                &api,
1539                cache,
1540                &mock_router(),
1541                &block,
1542                info,
1543                b"{}".to_vec(),
1544            )
1545        })
1546        .unwrap_err();
1547
1548        // Default error message from router when not found
1549        assert!(matches!(err.downcast().unwrap(), StdError::NotFound { .. }));
1550    }
1551
1552    #[test]
1553    fn query_contract_info() {
1554        let api = MockApi::default();
1555
1556        // prepare user addresses
1557        let creator_addr = api.addr_make("creator");
1558        let admin_addr = api.addr_make("admin");
1559
1560        let mut wasm_storage = MockStorage::new();
1561        let mut wasm_keeper = wasm_keeper();
1562        let block = mock_env().block;
1563        let code_id = wasm_keeper.store_code(creator_addr.clone(), payout::contract());
1564        assert_eq!(1, code_id);
1565
1566        let contract_addr = wasm_keeper
1567            .register_contract(
1568                &api,
1569                &mut wasm_storage,
1570                code_id,
1571                creator_addr.clone(),
1572                admin_addr.clone(),
1573                "label".to_owned(),
1574                1000,
1575                None,
1576            )
1577            .unwrap();
1578
1579        let querier: MockQuerier<Empty> = MockQuerier::new(&[]);
1580        let query = WasmQuery::ContractInfo {
1581            contract_addr: contract_addr.into(),
1582        };
1583
1584        let contract_info = wasm_keeper
1585            .query(&api, &wasm_storage, &querier, &block, query)
1586            .unwrap();
1587
1588        let actual: ContractInfoResponse = from_json(contract_info).unwrap();
1589        let expected =
1590            ContractInfoResponse::new(code_id, creator_addr, admin_addr.into(), false, None);
1591        assert_eq!(expected, actual);
1592    }
1593
1594    #[test]
1595    #[cfg(feature = "cosmwasm_1_2")]
1596    fn query_code_info() {
1597        let api = MockApi::default();
1598        let wasm_storage = MockStorage::new();
1599        let mut wasm_keeper = wasm_keeper();
1600        let block = mock_env().block;
1601        let creator_addr = api.addr_make("creator");
1602        let code_id = wasm_keeper.store_code(creator_addr.clone(), payout::contract());
1603        let querier: MockQuerier<Empty> = MockQuerier::new(&[]);
1604        let query = WasmQuery::CodeInfo { code_id };
1605        let code_info = wasm_keeper
1606            .query(&api, &wasm_storage, &querier, &block, query)
1607            .unwrap();
1608        let actual: CodeInfoResponse = from_json(code_info).unwrap();
1609        assert_eq!(code_id, actual.code_id);
1610        assert_eq!(creator_addr.as_str(), actual.creator.as_str());
1611        assert_eq!(32, actual.checksum.as_slice().len());
1612    }
1613
1614    #[test]
1615    #[cfg(feature = "cosmwasm_1_2")]
1616    fn different_contracts_must_have_different_checksum() {
1617        let api = MockApi::default();
1618        let creator_addr = api.addr_make("creator");
1619        let wasm_storage = MockStorage::new();
1620        let mut wasm_keeper = wasm_keeper();
1621        let block = mock_env().block;
1622        let code_id_payout = wasm_keeper.store_code(creator_addr.clone(), payout::contract());
1623        let code_id_caller = wasm_keeper.store_code(creator_addr, caller::contract());
1624        let querier: MockQuerier<Empty> = MockQuerier::new(&[]);
1625        let query_payout = WasmQuery::CodeInfo {
1626            code_id: code_id_payout,
1627        };
1628        let query_caller = WasmQuery::CodeInfo {
1629            code_id: code_id_caller,
1630        };
1631        let code_info_payout = wasm_keeper
1632            .query(&api, &wasm_storage, &querier, &block, query_payout)
1633            .unwrap();
1634        let code_info_caller = wasm_keeper
1635            .query(&api, &wasm_storage, &querier, &block, query_caller)
1636            .unwrap();
1637        let info_payout: CodeInfoResponse = from_json(code_info_payout).unwrap();
1638        let info_caller: CodeInfoResponse = from_json(code_info_caller).unwrap();
1639        assert_eq!(code_id_payout, info_payout.code_id);
1640        assert_eq!(code_id_caller, info_caller.code_id);
1641        assert_ne!(info_caller.code_id, info_payout.code_id);
1642        assert_eq!(info_caller.creator, info_payout.creator);
1643        assert_ne!(info_caller.checksum, info_payout.checksum);
1644    }
1645
1646    #[test]
1647    #[cfg(feature = "cosmwasm_1_2")]
1648    fn querying_invalid_code_info_must_fail() {
1649        let api = MockApi::default();
1650        let wasm_storage = MockStorage::new();
1651        let wasm_keeper = wasm_keeper();
1652        let block = mock_env().block;
1653
1654        let querier: MockQuerier<Empty> = MockQuerier::new(&[]);
1655        let query = WasmQuery::CodeInfo { code_id: 100 };
1656
1657        wasm_keeper
1658            .query(&api, &wasm_storage, &querier, &block, query)
1659            .unwrap_err();
1660    }
1661
1662    #[test]
1663    fn can_dump_raw_wasm_state() {
1664        let api = MockApi::default();
1665
1666        // prepare user addresses
1667        let creator_addr = api.addr_make("creator");
1668        let admin_addr = api.addr_make("admin");
1669        let user_addr = api.addr_make("foobar");
1670
1671        let mut wasm_keeper = wasm_keeper();
1672        let block = mock_env().block;
1673        let code_id = wasm_keeper.store_code(creator_addr, payout::contract());
1674
1675        let mut wasm_storage = MockStorage::new();
1676
1677        let contract_addr = wasm_keeper
1678            .register_contract(
1679                &api,
1680                &mut wasm_storage,
1681                code_id,
1682                user_addr.clone(),
1683                admin_addr,
1684                "label".to_owned(),
1685                1000,
1686                None,
1687            )
1688            .unwrap();
1689
1690        // make a contract with state
1691        let payout = coin(1500, "mlg");
1692        let msg = payout::InstantiateMessage {
1693            payout: payout.clone(),
1694        };
1695        wasm_keeper
1696            .call_instantiate(
1697                contract_addr.clone(),
1698                &api,
1699                &mut wasm_storage,
1700                &mock_router(),
1701                &block,
1702                message_info(&user_addr, &[]),
1703                to_json_vec(&msg).unwrap(),
1704            )
1705            .unwrap();
1706
1707        // dump state
1708        let state = wasm_keeper.dump_wasm_raw(&wasm_storage, &contract_addr);
1709        assert_eq!(state.len(), 2);
1710        // check contents
1711        let (k, v) = &state[0];
1712        assert_eq!(k.as_slice(), b"count");
1713        let count: u32 = from_json(v).unwrap();
1714        assert_eq!(count, 1);
1715        let (k, v) = &state[1];
1716        assert_eq!(k.as_slice(), b"payout");
1717        let stored_pay: payout::InstantiateMessage = from_json(v).unwrap();
1718        assert_eq!(stored_pay.payout, payout);
1719    }
1720
1721    #[test]
1722    fn contract_send_coins() {
1723        let api = MockApi::default();
1724
1725        // prepare user addresses
1726        let creator_addr = api.addr_make("creator");
1727        let user_addr = api.addr_make("foobar");
1728
1729        let mut wasm_keeper = wasm_keeper();
1730        let block = mock_env().block;
1731        let code_id = wasm_keeper.store_code(creator_addr, payout::contract());
1732
1733        let mut wasm_storage = MockStorage::new();
1734        let mut cache = StorageTransaction::new(&wasm_storage);
1735
1736        let contract_addr = wasm_keeper
1737            .register_contract(
1738                &api,
1739                &mut cache,
1740                code_id,
1741                user_addr.clone(),
1742                None,
1743                "label".to_owned(),
1744                1000,
1745                None,
1746            )
1747            .unwrap();
1748
1749        let payout = coin(100, "TGD");
1750
1751        // init the contract
1752        let info = message_info(&user_addr, &[]);
1753        let init_msg = to_json_vec(&payout::InstantiateMessage {
1754            payout: payout.clone(),
1755        })
1756        .unwrap();
1757        let res = wasm_keeper
1758            .call_instantiate(
1759                contract_addr.clone(),
1760                &api,
1761                &mut cache,
1762                &mock_router(),
1763                &block,
1764                info,
1765                init_msg,
1766            )
1767            .unwrap();
1768        assert_eq!(0, res.messages.len());
1769
1770        // execute the contract
1771        let info = message_info(&user_addr, &[]);
1772        let res = wasm_keeper
1773            .call_execute(
1774                &api,
1775                &mut cache,
1776                contract_addr.clone(),
1777                &mock_router(),
1778                &block,
1779                info,
1780                b"{}".to_vec(),
1781            )
1782            .unwrap();
1783        assert_eq!(1, res.messages.len());
1784        match &res.messages[0].msg {
1785            CosmosMsg::Bank(BankMsg::Send { to_address, amount }) => {
1786                assert_eq!(to_address.as_str(), user_addr.as_str());
1787                assert_eq!(amount.as_slice(), slice::from_ref(&payout));
1788            }
1789            m => panic!("Unexpected message {m:?}"),
1790        }
1791
1792        // and flush before query
1793        cache.prepare().commit(&mut wasm_storage);
1794
1795        // query the contract
1796        let query = to_json_vec(&payout::QueryMsg::Payout {}).unwrap();
1797        let querier: MockQuerier<Empty> = MockQuerier::new(&[]);
1798        let data = wasm_keeper
1799            .query_smart(contract_addr, &api, &wasm_storage, &querier, &block, query)
1800            .unwrap();
1801        let res: payout::InstantiateMessage = from_json(data).unwrap();
1802        assert_eq!(res.payout, payout);
1803    }
1804
1805    fn assert_payout(
1806        router: &WasmKeeper<Empty, Empty>,
1807        storage: &mut dyn Storage,
1808        contract_addr: &Addr,
1809        payout: &Coin,
1810    ) {
1811        let api = MockApi::default();
1812        let user_addr = api.addr_make("silly");
1813        let info = message_info(&user_addr, &[]);
1814        let res = router
1815            .call_execute(
1816                &api,
1817                storage,
1818                contract_addr.clone(),
1819                &mock_router(),
1820                &mock_env().block,
1821                info,
1822                b"{}".to_vec(),
1823            )
1824            .unwrap();
1825        assert_eq!(1, res.messages.len());
1826        match &res.messages[0].msg {
1827            CosmosMsg::Bank(BankMsg::Send { to_address, amount }) => {
1828                assert_eq!(to_address.as_str(), user_addr.as_str());
1829                assert_eq!(amount.as_slice(), slice::from_ref(payout));
1830            }
1831            m => panic!("Unexpected message {m:?}"),
1832        }
1833    }
1834
1835    fn assert_no_contract(storage: &dyn Storage, contract_addr: &Addr) {
1836        let contract = CONTRACTS.may_load(storage, contract_addr).unwrap();
1837        assert!(contract.is_none(), "{contract_addr:?}");
1838    }
1839
1840    #[test]
1841    fn multi_level_wasm_cache() {
1842        let api = MockApi::default();
1843
1844        // prepare user addresses
1845        let creator_addr = api.addr_make("creator");
1846        let user_addr = api.addr_make("foobar");
1847        let user_addr_1 = api.addr_make("johnny");
1848
1849        let mut wasm_keeper = wasm_keeper();
1850        let block = mock_env().block;
1851        let code_id = wasm_keeper.store_code(creator_addr, payout::contract());
1852
1853        let mut wasm_storage = MockStorage::new();
1854
1855        let payout1 = coin(100, "TGD");
1856
1857        // set contract 1 and commit (on router)
1858        let contract1 = transactional(&mut wasm_storage, |cache, _| {
1859            let contract = wasm_keeper
1860                .register_contract(
1861                    &api,
1862                    cache,
1863                    code_id,
1864                    user_addr.clone(),
1865                    None,
1866                    "".to_string(),
1867                    1000,
1868                    None,
1869                )
1870                .unwrap();
1871            let info = message_info(&user_addr, &[]);
1872            let init_msg = to_json_vec(&payout::InstantiateMessage {
1873                payout: payout1.clone(),
1874            })
1875            .unwrap();
1876            wasm_keeper
1877                .call_instantiate(
1878                    contract.clone(),
1879                    &api,
1880                    cache,
1881                    &mock_router(),
1882                    &block,
1883                    info,
1884                    init_msg,
1885                )
1886                .unwrap();
1887
1888            Ok(contract)
1889        })
1890        .unwrap();
1891
1892        let payout2 = coin(50, "BTC");
1893        let payout3 = coin(1234, "ATOM");
1894
1895        // create a new cache and check we can use contract 1
1896        let (contract2, contract3) = transactional(&mut wasm_storage, |cache, wasm_reader| {
1897            assert_payout(&wasm_keeper, cache, &contract1, &payout1);
1898
1899            // create contract 2 and use it
1900            let contract2 = wasm_keeper
1901                .register_contract(
1902                    &api,
1903                    cache,
1904                    code_id,
1905                    user_addr.clone(),
1906                    None,
1907                    "".to_owned(),
1908                    1000,
1909                    None,
1910                )
1911                .unwrap();
1912            let info = message_info(&user_addr, &[]);
1913            let init_msg = to_json_vec(&payout::InstantiateMessage {
1914                payout: payout2.clone(),
1915            })
1916            .unwrap();
1917            let _res = wasm_keeper
1918                .call_instantiate(
1919                    contract2.clone(),
1920                    &api,
1921                    cache,
1922                    &mock_router(),
1923                    &block,
1924                    info,
1925                    init_msg,
1926                )
1927                .unwrap();
1928            assert_payout(&wasm_keeper, cache, &contract2, &payout2);
1929
1930            // create a level2 cache and check we can use contract 1 and contract 2
1931            let contract3 = transactional(cache, |cache2, read| {
1932                assert_payout(&wasm_keeper, cache2, &contract1, &payout1);
1933                assert_payout(&wasm_keeper, cache2, &contract2, &payout2);
1934
1935                // create a contract on level 2
1936                let contract3 = wasm_keeper
1937                    .register_contract(
1938                        &api,
1939                        cache2,
1940                        code_id,
1941                        user_addr,
1942                        None,
1943                        "".to_owned(),
1944                        1000,
1945                        None,
1946                    )
1947                    .unwrap();
1948                let info = message_info(&user_addr_1, &[]);
1949                let init_msg = to_json_vec(&payout::InstantiateMessage {
1950                    payout: payout3.clone(),
1951                })
1952                .unwrap();
1953                let _res = wasm_keeper
1954                    .call_instantiate(
1955                        contract3.clone(),
1956                        &api,
1957                        cache2,
1958                        &mock_router(),
1959                        &block,
1960                        info,
1961                        init_msg,
1962                    )
1963                    .unwrap();
1964                assert_payout(&wasm_keeper, cache2, &contract3, &payout3);
1965
1966                // ensure first cache still doesn't see this contract
1967                assert_no_contract(read, &contract3);
1968                Ok(contract3)
1969            })
1970            .unwrap();
1971
1972            // after applying transaction, all contracts present on cache
1973            assert_payout(&wasm_keeper, cache, &contract1, &payout1);
1974            assert_payout(&wasm_keeper, cache, &contract2, &payout2);
1975            assert_payout(&wasm_keeper, cache, &contract3, &payout3);
1976
1977            // but not yet the root router
1978            assert_no_contract(wasm_reader, &contract1);
1979            assert_no_contract(wasm_reader, &contract2);
1980            assert_no_contract(wasm_reader, &contract3);
1981
1982            Ok((contract2, contract3))
1983        })
1984        .unwrap();
1985
1986        // ensure that it is now applied to the router
1987        assert_payout(&wasm_keeper, &mut wasm_storage, &contract1, &payout1);
1988        assert_payout(&wasm_keeper, &mut wasm_storage, &contract2, &payout2);
1989        assert_payout(&wasm_keeper, &mut wasm_storage, &contract3, &payout3);
1990    }
1991
1992    fn assert_admin(
1993        storage: &dyn Storage,
1994        wasm_keeper: &WasmKeeper<Empty, Empty>,
1995        contract_addr: &impl ToString,
1996        admin: Option<Addr>,
1997    ) {
1998        let api = MockApi::default();
1999        let querier: MockQuerier<Empty> = MockQuerier::new(&[]);
2000        // query
2001        let data = wasm_keeper
2002            .query(
2003                &api,
2004                storage,
2005                &querier,
2006                &mock_env().block,
2007                WasmQuery::ContractInfo {
2008                    contract_addr: contract_addr.to_string(),
2009                },
2010            )
2011            .unwrap();
2012        let res: ContractInfoResponse = from_json(data).unwrap();
2013        assert_eq!(res.admin, admin);
2014    }
2015
2016    #[test]
2017    fn update_clear_admin_works() {
2018        let api = MockApi::default();
2019        let mut wasm_keeper = wasm_keeper();
2020        let block = mock_env().block;
2021        let creator = api.addr_make("creator");
2022        let code_id = wasm_keeper.store_code(creator.clone(), caller::contract());
2023
2024        let mut wasm_storage = MockStorage::new();
2025
2026        let admin = api.addr_make("admin");
2027        let new_admin = api.addr_make("new_admin");
2028        let normal_user = api.addr_make("normal_user");
2029
2030        let contract_addr = wasm_keeper
2031            .register_contract(
2032                &api,
2033                &mut wasm_storage,
2034                code_id,
2035                creator,
2036                admin.clone(),
2037                "label".to_owned(),
2038                1000,
2039                None,
2040            )
2041            .unwrap();
2042
2043        // init the contract
2044        let info = message_info(&admin, &[]);
2045        let init_msg = to_json_vec(&Empty {}).unwrap();
2046        let res = wasm_keeper
2047            .call_instantiate(
2048                contract_addr.clone(),
2049                &api,
2050                &mut wasm_storage,
2051                &mock_router(),
2052                &block,
2053                info,
2054                init_msg,
2055            )
2056            .unwrap();
2057        assert_eq!(0, res.messages.len());
2058
2059        assert_admin(
2060            &wasm_storage,
2061            &wasm_keeper,
2062            &contract_addr,
2063            Some(admin.clone()),
2064        );
2065
2066        // non-admin should not be allowed to become admin on their own
2067        wasm_keeper
2068            .execute_wasm(
2069                &api,
2070                &mut wasm_storage,
2071                &mock_router(),
2072                &block,
2073                normal_user.clone(),
2074                WasmMsg::UpdateAdmin {
2075                    contract_addr: contract_addr.to_string(),
2076                    admin: normal_user.to_string(),
2077                },
2078            )
2079            .unwrap_err();
2080
2081        // should still be admin
2082        assert_admin(
2083            &wasm_storage,
2084            &wasm_keeper,
2085            &contract_addr,
2086            Some(admin.clone()),
2087        );
2088
2089        // admin should be allowed to transfer administration permissions
2090        let res = wasm_keeper
2091            .execute_wasm(
2092                &api,
2093                &mut wasm_storage,
2094                &mock_router(),
2095                &block,
2096                admin,
2097                WasmMsg::UpdateAdmin {
2098                    contract_addr: contract_addr.to_string(),
2099                    admin: new_admin.to_string(),
2100                },
2101            )
2102            .unwrap();
2103        assert_eq!(res.events.len(), 0);
2104
2105        // new_admin should now be admin
2106        assert_admin(
2107            &wasm_storage,
2108            &wasm_keeper,
2109            &contract_addr,
2110            Some(new_admin.clone()),
2111        );
2112
2113        // new_admin should now be able to clear to admin
2114        let res = wasm_keeper
2115            .execute_wasm(
2116                &api,
2117                &mut wasm_storage,
2118                &mock_router(),
2119                &block,
2120                new_admin,
2121                WasmMsg::ClearAdmin {
2122                    contract_addr: contract_addr.to_string(),
2123                },
2124            )
2125            .unwrap();
2126        assert_eq!(res.events.len(), 0);
2127
2128        // should have no admin now
2129        assert_admin(&wasm_storage, &wasm_keeper, &contract_addr, None);
2130    }
2131
2132    #[test]
2133    fn uses_simple_address_generator_by_default() {
2134        let api = MockApi::default();
2135        let mut wasm_keeper = wasm_keeper();
2136        let creator_addr = api.addr_make("creator");
2137        let code_id = wasm_keeper.store_code(creator_addr.clone(), payout::contract());
2138        assert_eq!(1, code_id);
2139
2140        let mut wasm_storage = MockStorage::new();
2141
2142        let admin = api.addr_make("admin");
2143        let contract_addr = wasm_keeper
2144            .register_contract(
2145                &api,
2146                &mut wasm_storage,
2147                code_id,
2148                creator_addr.clone(),
2149                admin.clone(),
2150                "label".to_owned(),
2151                1000,
2152                None,
2153            )
2154            .unwrap();
2155
2156        assert_eq!(
2157            contract_addr.as_str(),
2158            "cosmwasm1mzdhwvvh22wrt07w59wxyd58822qavwkx5lcej7aqfkpqqlhaqfsgn6fq2",
2159            "default address generator returned incorrect address"
2160        );
2161
2162        let salt = HexBinary::from_hex("c0ffee").unwrap();
2163
2164        let contract_addr = wasm_keeper
2165            .register_contract(
2166                &api,
2167                &mut wasm_storage,
2168                code_id,
2169                creator_addr.clone(),
2170                admin.clone(),
2171                "label".to_owned(),
2172                1000,
2173                Binary::from(salt.clone()),
2174            )
2175            .unwrap();
2176
2177        assert_eq!(
2178            contract_addr.as_str(),
2179            "cosmwasm1drhu6t78wacgm5qjzs4hvkv9fd9awa9henw7fh6vmzrhf7k2nkjsg3flns",
2180            "default address generator returned incorrect address"
2181        );
2182
2183        let code_id = wasm_keeper.store_code(creator_addr, payout::contract());
2184        assert_eq!(2, code_id);
2185
2186        let user_addr = api.addr_make("boobaz");
2187
2188        let contract_addr = wasm_keeper
2189            .register_contract(
2190                &api,
2191                &mut wasm_storage,
2192                code_id,
2193                user_addr,
2194                admin,
2195                "label".to_owned(),
2196                1000,
2197                Binary::from(salt),
2198            )
2199            .unwrap();
2200
2201        assert_eq!(
2202            contract_addr.as_str(),
2203            "cosmwasm13cfeertf2gny0rzp5jwqzst8crmfgvcd2lq5su0c9z66yxa45qdsdd0uxc",
2204            "default address generator returned incorrect address"
2205        );
2206    }
2207
2208    struct TestAddressGenerator {
2209        address: Addr,
2210        predictable_address: Addr,
2211    }
2212
2213    impl AddressGenerator for TestAddressGenerator {
2214        fn contract_address(
2215            &self,
2216            _api: &dyn Api,
2217            _storage: &mut dyn Storage,
2218            _code_id: u64,
2219            _instance_id: u64,
2220        ) -> AnyResult<Addr> {
2221            Ok(self.address.clone())
2222        }
2223
2224        fn predictable_contract_address(
2225            &self,
2226            _api: &dyn Api,
2227            _storage: &mut dyn Storage,
2228            _code_id: u64,
2229            _instance_id: u64,
2230            _checksum: &[u8],
2231            _creator: &CanonicalAddr,
2232            _salt: &[u8],
2233        ) -> AnyResult<Addr> {
2234            Ok(self.predictable_address.clone())
2235        }
2236    }
2237
2238    #[test]
2239    fn can_use_custom_address_generator() {
2240        let api = MockApi::default();
2241        let expected_addr = api.addr_make("address");
2242        let expected_predictable_addr = api.addr_make("predictable_address");
2243        let mut wasm_keeper: WasmKeeper<Empty, Empty> =
2244            WasmKeeper::new().with_address_generator(TestAddressGenerator {
2245                address: expected_addr.clone(),
2246                predictable_address: expected_predictable_addr.clone(),
2247            });
2248        let creator = api.addr_make("creator");
2249        let code_id = wasm_keeper.store_code(creator.clone(), payout::contract());
2250
2251        let mut wasm_storage = MockStorage::new();
2252
2253        let admin = api.addr_make("admin");
2254        let contract_addr = wasm_keeper
2255            .register_contract(
2256                &api,
2257                &mut wasm_storage,
2258                code_id,
2259                creator.clone(),
2260                admin.clone(),
2261                "label".to_owned(),
2262                1000,
2263                None,
2264            )
2265            .unwrap();
2266
2267        assert_eq!(
2268            contract_addr, expected_addr,
2269            "custom address generator returned incorrect address"
2270        );
2271
2272        let contract_addr = wasm_keeper
2273            .register_contract(
2274                &api,
2275                &mut wasm_storage,
2276                code_id,
2277                creator,
2278                admin,
2279                "label".to_owned(),
2280                1000,
2281                Binary::from(HexBinary::from_hex("23A74B8C").unwrap()),
2282            )
2283            .unwrap();
2284
2285        assert_eq!(
2286            contract_addr, expected_predictable_addr,
2287            "custom address generator returned incorrect address"
2288        );
2289    }
2290}