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        // Prepare the message type URL, will be needed when calling `reply` entrypoint.
865        let 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 reply = Reply {
876                    id,
877                    payload,
878                    gas_used: 0,
879                    result: SubMsgResult::Ok(
880                        #[allow(deprecated)]
881                        SubMsgResponse {
882                            events: r.events.clone(),
883                            data: r.data.clone(),
884                            msg_responses: vec![MsgResponse {
885                                type_url,
886                                value: r.data.unwrap_or_default(),
887                            }],
888                        },
889                    ),
890                };
891                // do reply and combine it with the original response
892                let reply_res = self.reply(api, router, storage, block, contract, reply)?;
893                // override data
894                r.data = reply_res.data;
895                // append the events
896                r.events.extend_from_slice(&reply_res.events);
897            } else {
898                // reply is not called, no data should be returned
899                r.data = None;
900            }
901            Ok(r)
902        } else if let Err(e) = sub_message_result {
903            if matches!(reply_on, ReplyOn::Always | ReplyOn::Error) {
904                let reply = Reply {
905                    id,
906                    payload,
907                    gas_used: 0,
908                    result: SubMsgResult::Err(format!("{e:?}")),
909                };
910                self.reply(api, router, storage, block, contract, reply)
911            } else {
912                Err(e)
913            }
914        } else {
915            sub_message_result
916        }
917    }
918
919    fn reply(
920        &self,
921        api: &dyn Api,
922        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
923        storage: &mut dyn Storage,
924        block: &BlockInfo,
925        contract: Addr,
926        reply: Reply,
927    ) -> AnyResult<AppResponse> {
928        let ok_attr = if reply.result.is_ok() {
929            "handle_success"
930        } else {
931            "handle_failure"
932        };
933        let custom_event = Event::new("reply")
934            .add_attribute(CONTRACT_ATTR, &contract)
935            .add_attribute("mode", ok_attr);
936
937        let res = self.call_reply(contract.clone(), api, storage, router, block, reply)?;
938        let (res, msgs) = self.build_app_response(&contract, custom_event, res);
939        self.process_response(api, router, storage, block, contract, res, msgs)
940    }
941
942    /// Captures all the events, data and sub messages from the contract call.
943    ///
944    /// This function does not handle the messages.
945    fn build_app_response(
946        &self,
947        contract: &Addr,
948        custom_event: Event, // entry-point specific custom event added by x/wasm
949        response: Response<ExecC>,
950    ) -> (AppResponse, Vec<SubMsg<ExecC>>) {
951        let Response {
952            messages,
953            attributes,
954            events,
955            data,
956            ..
957        } = response;
958
959        // always add custom event
960        let mut app_events = Vec::with_capacity(2 + events.len());
961        app_events.push(custom_event);
962
963        // we only emit the `wasm` event if some attributes are specified
964        if !attributes.is_empty() {
965            // turn attributes into event and place it first
966            let wasm_event = Event::new("wasm")
967                .add_attribute(CONTRACT_ATTR, contract)
968                .add_attributes(attributes);
969            app_events.push(wasm_event);
970        }
971
972        // These need to get `wasm-` prefix to match the wasmd semantics (custom wasm messages cannot
973        // fake system level event types, like transfer from the bank module)
974        let wasm_events = events.into_iter().map(|mut ev| {
975            ev.ty = format!("wasm-{}", ev.ty);
976            ev.attributes
977                .insert(0, mock_wasmd_attr(CONTRACT_ATTR, contract));
978            ev
979        });
980        app_events.extend(wasm_events);
981
982        let app_response = AppResponse {
983            events: app_events,
984            data,
985        };
986        (app_response, messages)
987    }
988
989    fn process_response(
990        &self,
991        api: &dyn Api,
992        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
993        storage: &mut dyn Storage,
994        block: &BlockInfo,
995        contract: Addr,
996        response: AppResponse,
997        sub_messages: Vec<SubMsg<ExecC>>,
998    ) -> AnyResult<AppResponse> {
999        // Unpack the provided response.
1000        let AppResponse {
1001            mut events, data, ..
1002        } = response;
1003        // Recurse in all submessages.
1004        let data = sub_messages
1005            .into_iter()
1006            .try_fold(data, |data, sub_message| {
1007                // Execute the submessage.
1008                let sub_response = self.execute_submsg(
1009                    api,
1010                    router,
1011                    storage,
1012                    block,
1013                    contract.clone(),
1014                    sub_message,
1015                )?;
1016                // COLLECT and append all events from the processed submessage.
1017                events.extend_from_slice(&sub_response.events);
1018                // REPLACE the data with value from the processes submessage (if not empty).
1019                Ok::<_, AnyError>(sub_response.data.or(data))
1020            })?;
1021        // Return the response with updated data, events and message responses taken from
1022        // all processed sub messages. Note that events and message responses are collected,
1023        // but the data is replaced with the data from the last processes submessage.
1024        Ok(AppResponse { events, data })
1025    }
1026
1027    /// Creates a contract address and empty storage instance.
1028    /// Returns the new contract address.
1029    ///
1030    /// You have to call init after this to set up the contract properly.
1031    /// These two steps are separated to have cleaner return values.
1032    pub fn register_contract(
1033        &self,
1034        api: &dyn Api,
1035        storage: &mut dyn Storage,
1036        code_id: u64,
1037        creator: Addr,
1038        admin: impl Into<Option<Addr>>,
1039        label: String,
1040        created: u64,
1041        salt: impl Into<Option<Binary>>,
1042    ) -> AnyResult<Addr> {
1043        // check if the contract's code with specified code_id exists
1044        if code_id as usize > self.code_data.len() {
1045            bail!("Cannot init contract with unregistered code id");
1046        }
1047
1048        // generate a new contract address
1049        let instance_id = self.instance_count(storage) as u64;
1050        let addr = if let Some(salt_binary) = salt.into() {
1051            // generate predictable contract address when salt is provided
1052            let code_data = self.code_data(code_id)?;
1053            let canonical_addr = &api.addr_canonicalize(creator.as_ref())?;
1054            self.address_generator.predictable_contract_address(
1055                api,
1056                storage,
1057                code_id,
1058                instance_id,
1059                code_data.checksum.as_slice(),
1060                canonical_addr,
1061                salt_binary.as_slice(),
1062            )?
1063        } else {
1064            // generate non-predictable contract address
1065            self.address_generator
1066                .contract_address(api, storage, code_id, instance_id)?
1067        };
1068
1069        // contract with the same address must not already exist
1070        if self.contract_data(storage, &addr).is_ok() {
1071            bail!(Error::duplicated_contract_address(addr));
1072        }
1073
1074        // prepare contract data and save new contract instance
1075        let info = ContractData {
1076            code_id,
1077            creator,
1078            admin: admin.into(),
1079            label,
1080            created,
1081        };
1082        self.save_contract(storage, &addr, &info)?;
1083        Ok(addr)
1084    }
1085
1086    /// Executes contract's `execute` entry-point.
1087    pub fn call_execute(
1088        &self,
1089        api: &dyn Api,
1090        storage: &mut dyn Storage,
1091        address: Addr,
1092        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
1093        block: &BlockInfo,
1094        info: MessageInfo,
1095        msg: Vec<u8>,
1096    ) -> AnyResult<Response<ExecC>> {
1097        Self::verify_response(self.with_storage(
1098            api,
1099            storage,
1100            router,
1101            block,
1102            address,
1103            |contract, deps, env| contract.execute(deps, env, info, msg),
1104        )?)
1105    }
1106
1107    /// Executes contract's `instantiate` entry-point.
1108    pub fn call_instantiate(
1109        &self,
1110        address: Addr,
1111        api: &dyn Api,
1112        storage: &mut dyn Storage,
1113        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
1114        block: &BlockInfo,
1115        info: MessageInfo,
1116        msg: Vec<u8>,
1117    ) -> AnyResult<Response<ExecC>> {
1118        Self::verify_response(self.with_storage(
1119            api,
1120            storage,
1121            router,
1122            block,
1123            address,
1124            |contract, deps, env| contract.instantiate(deps, env, info, msg),
1125        )?)
1126    }
1127
1128    /// Executes contract's `reply` entry-point.
1129    pub fn call_reply(
1130        &self,
1131        address: Addr,
1132        api: &dyn Api,
1133        storage: &mut dyn Storage,
1134        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
1135        block: &BlockInfo,
1136        reply: Reply,
1137    ) -> AnyResult<Response<ExecC>> {
1138        Self::verify_response(self.with_storage(
1139            api,
1140            storage,
1141            router,
1142            block,
1143            address,
1144            |contract, deps, env| contract.reply(deps, env, reply),
1145        )?)
1146    }
1147
1148    /// Executes contract's `sudo` entry-point.
1149    pub fn call_sudo(
1150        &self,
1151        address: Addr,
1152        api: &dyn Api,
1153        storage: &mut dyn Storage,
1154        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
1155        block: &BlockInfo,
1156        msg: Vec<u8>,
1157    ) -> AnyResult<Response<ExecC>> {
1158        Self::verify_response(self.with_storage(
1159            api,
1160            storage,
1161            router,
1162            block,
1163            address,
1164            |contract, deps, env| contract.sudo(deps, env, msg),
1165        )?)
1166    }
1167
1168    /// Executes contract's `migrate` entry-point.
1169    #[cfg(not(feature = "cosmwasm_2_2"))]
1170    pub fn call_migrate(
1171        &self,
1172        address: Addr,
1173        api: &dyn Api,
1174        storage: &mut dyn Storage,
1175        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
1176        block: &BlockInfo,
1177        msg: Vec<u8>,
1178    ) -> AnyResult<Response<ExecC>> {
1179        Self::verify_response(self.with_storage(
1180            api,
1181            storage,
1182            router,
1183            block,
1184            address,
1185            |contract, deps, env| contract.migrate(deps, env, msg),
1186        )?)
1187    }
1188
1189    /// Executes contract's `migrate` entry-point.
1190    #[cfg(feature = "cosmwasm_2_2")]
1191    pub fn call_migrate(
1192        &self,
1193        address: Addr,
1194        api: &dyn Api,
1195        storage: &mut dyn Storage,
1196        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
1197        block: &BlockInfo,
1198        msg: Vec<u8>,
1199        info: MigrateInfo,
1200    ) -> AnyResult<Response<ExecC>> {
1201        Self::verify_response(self.with_storage(
1202            api,
1203            storage,
1204            router,
1205            block,
1206            address,
1207            |contract, deps, env| contract.migrate(deps, env, msg, info),
1208        )?)
1209    }
1210
1211    fn get_env<T: Into<Addr>>(&self, address: T, block: &BlockInfo) -> Env {
1212        Env {
1213            block: block.clone(),
1214            contract: ContractInfo {
1215                address: address.into(),
1216            },
1217            transaction: Some(TransactionInfo { index: 0 }),
1218        }
1219    }
1220
1221    fn with_storage_readonly<F, T>(
1222        &self,
1223        api: &dyn Api,
1224        storage: &dyn Storage,
1225        querier: &dyn Querier,
1226        block: &BlockInfo,
1227        address: Addr,
1228        action: F,
1229    ) -> AnyResult<T>
1230    where
1231        F: FnOnce(&dyn Contract<ExecC, QueryC>, Deps<QueryC>, Env) -> AnyResult<T>,
1232    {
1233        let contract = self.contract_data(storage, &address)?;
1234        let handler = self.contract_code(contract.code_id)?;
1235        let storage = self.contract_storage(storage, &address);
1236        let env = self.get_env(address, block);
1237
1238        let deps = Deps {
1239            storage: storage.as_ref(),
1240            api,
1241            querier: QuerierWrapper::new(querier),
1242        };
1243        action(handler, deps, env)
1244    }
1245
1246    fn with_storage<F, T>(
1247        &self,
1248        api: &dyn Api,
1249        storage: &mut dyn Storage,
1250        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
1251        block: &BlockInfo,
1252        address: Addr,
1253        action: F,
1254    ) -> AnyResult<T>
1255    where
1256        F: FnOnce(&dyn Contract<ExecC, QueryC>, DepsMut<QueryC>, Env) -> AnyResult<T>,
1257        ExecC: DeserializeOwned,
1258    {
1259        let contract = self.contract_data(storage, &address)?;
1260        let handler = self.contract_code(contract.code_id)?;
1261
1262        // We don't actually need a transaction here, as it is already embedded in a transactional.
1263        // execute_submsg or App.execute_multi.
1264        // However, we need to get write and read access to the same storage in two different objects,
1265        // and this is the only way I know how to do so.
1266        transactional(storage, |write_cache, read_store| {
1267            let mut contract_storage = self.contract_storage_mut(write_cache, &address);
1268            let querier = RouterQuerier::new(router, api, read_store, block);
1269            let env = self.get_env(address, block);
1270
1271            let deps = DepsMut {
1272                storage: contract_storage.as_mut(),
1273                api,
1274                querier: QuerierWrapper::new(&querier),
1275            };
1276            action(handler, deps, env)
1277        })
1278    }
1279
1280    /// Saves contract data in a storage under specified address.
1281    pub fn save_contract(
1282        &self,
1283        storage: &mut dyn Storage,
1284        address: &Addr,
1285        contract: &ContractData,
1286    ) -> AnyResult<()> {
1287        let mut storage: TypedPrefixedStorageMut<'_, WasmKeeper<ExecC, QueryC>> =
1288            WasmStorageMut::new(storage);
1289        CONTRACTS
1290            .save(&mut storage, address, contract)
1291            .map_err(Into::into)
1292    }
1293
1294    /// Returns the number of all contract instances.
1295    fn instance_count(&self, storage: &dyn Storage) -> usize {
1296        let storage: TypedPrefixedStorage<'_, WasmKeeper<ExecC, QueryC>> =
1297            WasmStorage::new(storage);
1298        CONTRACTS
1299            .range_raw(&storage, None, None, Order::Ascending)
1300            .count()
1301    }
1302
1303    /// Returns the response type for specified message.
1304    fn response_type_url(msg: &CosmosMsg<ExecC>) -> String {
1305        const UNKNOWN: &str = "/unknown";
1306        match &msg {
1307            CosmosMsg::Bank(bank_msg) => match bank_msg {
1308                BankMsg::Send { .. } => "/cosmos.bank.v1beta1.MsgSendResponse",
1309                BankMsg::Burn { .. } => "/cosmos.bank.v1beta1.MsgBurnResponse",
1310                _ => UNKNOWN,
1311            },
1312            CosmosMsg::Custom(..) => UNKNOWN,
1313            #[cfg(feature = "staking")]
1314            CosmosMsg::Staking(staking_msg) => match staking_msg {
1315                StakingMsg::Delegate { .. } => "/cosmos.staking.v1beta1.MsgDelegateResponse",
1316                StakingMsg::Undelegate { .. } => "/cosmos.staking.v1beta1.MsgUndelegateResponse",
1317                StakingMsg::Redelegate { .. } => {
1318                    "/cosmos.staking.v1beta1.MsgBeginRedelegateResponse"
1319                }
1320                _ => UNKNOWN,
1321            },
1322            #[cfg(feature = "staking")]
1323            CosmosMsg::Distribution(distribution_msg) => match distribution_msg {
1324                #[cfg(feature = "cosmwasm_1_3")]
1325                DistributionMsg::FundCommunityPool { .. } => {
1326                    "/cosmos.distribution.v1beta1.MsgFundCommunityPoolResponse"
1327                }
1328                DistributionMsg::SetWithdrawAddress { .. } => {
1329                    "/cosmos.distribution.v1beta1.MsgSetWithdrawAddressResponse"
1330                }
1331                DistributionMsg::WithdrawDelegatorReward { .. } => {
1332                    "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorRewardResponse"
1333                }
1334                _ => UNKNOWN,
1335            },
1336            #[cfg(feature = "stargate")]
1337            #[allow(deprecated)]
1338            CosmosMsg::Stargate { .. } => UNKNOWN,
1339            #[cfg(feature = "cosmwasm_2_0")]
1340            CosmosMsg::Any(..) => UNKNOWN,
1341            #[cfg(feature = "stargate")]
1342            CosmosMsg::Ibc(..) => UNKNOWN,
1343            CosmosMsg::Wasm(wasm_msg) => match wasm_msg {
1344                WasmMsg::Instantiate { .. } => "/cosmwasm.wasm.v1.MsgInstantiateContractResponse",
1345                #[cfg(feature = "cosmwasm_1_2")]
1346                WasmMsg::Instantiate2 { .. } => "/cosmwasm.wasm.v1.MsgInstantiateContract2Response",
1347                WasmMsg::Execute { .. } => "/cosmwasm.wasm.v1.MsgExecuteContractResponse",
1348                WasmMsg::Migrate { .. } => "/cosmwasm.wasm.v1.MsgMigrateContractResponse",
1349                WasmMsg::UpdateAdmin { .. } => "/cosmwasm.wasm.v1.MsgUpdateAdminResponse",
1350                WasmMsg::ClearAdmin { .. } => "/cosmwasm.wasm.v1.MsgClearAdminResponse",
1351                _ => UNKNOWN,
1352            },
1353            #[cfg(feature = "stargate")]
1354            CosmosMsg::Gov(gov_msg) => match gov_msg {
1355                GovMsg::Vote { .. } => "/cosmos.gov.v1beta1.MsgVoteResponse",
1356                #[cfg(feature = "cosmwasm_1_2")]
1357                GovMsg::VoteWeighted { .. } => "/cosmos.gov.v1beta1.MsgVoteWeightedResponse",
1358            },
1359            _ => UNKNOWN,
1360        }
1361        .to_string()
1362    }
1363}
1364
1365#[derive(Clone, PartialEq, Message)]
1366struct InstantiateResponse {
1367    #[prost(string, tag = "1")]
1368    pub address: String,
1369    #[prost(bytes, tag = "2")]
1370    pub data: Vec<u8>,
1371}
1372
1373fn instantiate_response(data: Option<Binary>, contact_address: &Addr) -> Binary {
1374    let data = data.unwrap_or_default().to_vec();
1375    let init_data = InstantiateResponse {
1376        address: contact_address.into(),
1377        data,
1378    };
1379    let mut new_data = Vec::<u8>::with_capacity(init_data.encoded_len());
1380    // the data must encode successfully
1381    init_data.encode(&mut new_data).unwrap();
1382    new_data.into()
1383}
1384
1385#[derive(Clone, PartialEq, Message)]
1386struct ExecuteResponse {
1387    #[prost(bytes, tag = "1")]
1388    pub data: Vec<u8>,
1389}
1390
1391/// Encodes the response data.
1392fn encode_response_data(data: Option<Binary>) -> Option<Binary> {
1393    data.map(|d| {
1394        let execute_response = ExecuteResponse { data: d.to_vec() };
1395        let mut encoded_data = Vec::<u8>::with_capacity(execute_response.encoded_len());
1396        execute_response.encode(&mut encoded_data).unwrap();
1397        encoded_data.into()
1398    })
1399}
1400
1401#[cfg(test)]
1402mod test {
1403    use super::*;
1404    use crate::app::Router;
1405    use crate::bank::BankKeeper;
1406    use crate::featured::staking::{DistributionKeeper, StakeKeeper};
1407    use crate::module::FailingModule;
1408    use crate::test_helpers::{caller, error, payout};
1409    use crate::transactions::StorageTransaction;
1410    use crate::{GovFailingModule, IbcFailingModule, StargateFailing};
1411    use cosmwasm_std::testing::{message_info, mock_env, MockApi, MockQuerier, MockStorage};
1412    #[cfg(feature = "cosmwasm_1_2")]
1413    use cosmwasm_std::CodeInfoResponse;
1414    use cosmwasm_std::{
1415        coin, from_json, to_json_vec, CanonicalAddr, CosmosMsg, Empty, HexBinary, StdError,
1416    };
1417
1418    /// Type alias for default build `Router` to make its reference in typical scenario
1419    type BasicRouter<ExecC = Empty, QueryC = Empty> = Router<
1420        BankKeeper,
1421        FailingModule<ExecC, QueryC, Empty>,
1422        WasmKeeper<ExecC, QueryC>,
1423        StakeKeeper,
1424        DistributionKeeper,
1425        IbcFailingModule,
1426        GovFailingModule,
1427        StargateFailing,
1428    >;
1429
1430    fn wasm_keeper() -> WasmKeeper<Empty, Empty> {
1431        WasmKeeper::new()
1432    }
1433
1434    fn mock_router() -> BasicRouter {
1435        Router {
1436            wasm: WasmKeeper::new(),
1437            bank: BankKeeper::new(),
1438            custom: FailingModule::new(),
1439            staking: StakeKeeper::new(),
1440            distribution: DistributionKeeper::new(),
1441            ibc: IbcFailingModule::new(),
1442            gov: GovFailingModule::new(),
1443            stargate: StargateFailing,
1444        }
1445    }
1446
1447    #[test]
1448    fn register_contract() {
1449        let api = MockApi::default();
1450
1451        // prepare user addresses
1452        let creator_addr = api.addr_make("creator");
1453        let user_addr = api.addr_make("foobar");
1454        let admin_addr = api.addr_make("admin");
1455        let unregistered_addr = api.addr_make("unregistered");
1456
1457        let mut wasm_storage = MockStorage::new();
1458        let mut wasm_keeper = wasm_keeper();
1459        let block = mock_env().block;
1460        let code_id = wasm_keeper.store_code(creator_addr, error::contract(false));
1461
1462        transactional(&mut wasm_storage, |cache, _| {
1463            // cannot register contract with unregistered codeId
1464            wasm_keeper.register_contract(
1465                &api,
1466                cache,
1467                code_id + 1,
1468                user_addr.clone(),
1469                admin_addr.clone(),
1470                "label".to_owned(),
1471                1000,
1472                None,
1473            )
1474        })
1475        .unwrap_err();
1476
1477        let contract_addr = transactional(&mut wasm_storage, |cache, _| {
1478            // we can register a new instance of this code
1479            wasm_keeper.register_contract(
1480                &api,
1481                cache,
1482                code_id,
1483                user_addr.clone(),
1484                admin_addr.clone(),
1485                "label".to_owned(),
1486                1000,
1487                None,
1488            )
1489        })
1490        .unwrap();
1491
1492        // verify contract data are as expected
1493        let contract_data = wasm_keeper
1494            .contract_data(&wasm_storage, &contract_addr)
1495            .unwrap();
1496
1497        assert_eq!(
1498            contract_data,
1499            ContractData {
1500                code_id,
1501                creator: user_addr.clone(),
1502                admin: admin_addr.into(),
1503                label: "label".to_owned(),
1504                created: 1000,
1505            }
1506        );
1507
1508        let err = transactional(&mut wasm_storage, |cache, _| {
1509            // now, we call this contract and see the error message from the contract
1510            let info = message_info(&user_addr, &[]);
1511            wasm_keeper.call_instantiate(
1512                contract_addr.clone(),
1513                &api,
1514                cache,
1515                &mock_router(),
1516                &block,
1517                info,
1518                b"{}".to_vec(),
1519            )
1520        })
1521        .unwrap_err();
1522
1523        // StdError from contract_error auto-converted to string
1524        assert_eq!(
1525            StdError::generic_err("Init failed"),
1526            err.downcast().unwrap()
1527        );
1528
1529        let err = transactional(&mut wasm_storage, |cache, _| {
1530            // and the error for calling an unregistered contract
1531            let info = message_info(&user_addr, &[]);
1532            wasm_keeper.call_instantiate(
1533                unregistered_addr,
1534                &api,
1535                cache,
1536                &mock_router(),
1537                &block,
1538                info,
1539                b"{}".to_vec(),
1540            )
1541        })
1542        .unwrap_err();
1543
1544        // Default error message from router when not found
1545        assert!(matches!(err.downcast().unwrap(), StdError::NotFound { .. }));
1546    }
1547
1548    #[test]
1549    fn query_contract_info() {
1550        let api = MockApi::default();
1551
1552        // prepare user addresses
1553        let creator_addr = api.addr_make("creator");
1554        let admin_addr = api.addr_make("admin");
1555
1556        let mut wasm_storage = MockStorage::new();
1557        let mut wasm_keeper = wasm_keeper();
1558        let block = mock_env().block;
1559        let code_id = wasm_keeper.store_code(creator_addr.clone(), payout::contract());
1560        assert_eq!(1, code_id);
1561
1562        let contract_addr = wasm_keeper
1563            .register_contract(
1564                &api,
1565                &mut wasm_storage,
1566                code_id,
1567                creator_addr.clone(),
1568                admin_addr.clone(),
1569                "label".to_owned(),
1570                1000,
1571                None,
1572            )
1573            .unwrap();
1574
1575        let querier: MockQuerier<Empty> = MockQuerier::new(&[]);
1576        let query = WasmQuery::ContractInfo {
1577            contract_addr: contract_addr.into(),
1578        };
1579
1580        let contract_info = wasm_keeper
1581            .query(&api, &wasm_storage, &querier, &block, query)
1582            .unwrap();
1583
1584        let actual: ContractInfoResponse = from_json(contract_info).unwrap();
1585        let expected =
1586            ContractInfoResponse::new(code_id, creator_addr, admin_addr.into(), false, None);
1587        assert_eq!(expected, actual);
1588    }
1589
1590    #[test]
1591    #[cfg(feature = "cosmwasm_1_2")]
1592    fn query_code_info() {
1593        let api = MockApi::default();
1594        let wasm_storage = MockStorage::new();
1595        let mut wasm_keeper = wasm_keeper();
1596        let block = mock_env().block;
1597        let creator_addr = api.addr_make("creator");
1598        let code_id = wasm_keeper.store_code(creator_addr.clone(), payout::contract());
1599        let querier: MockQuerier<Empty> = MockQuerier::new(&[]);
1600        let query = WasmQuery::CodeInfo { code_id };
1601        let code_info = wasm_keeper
1602            .query(&api, &wasm_storage, &querier, &block, query)
1603            .unwrap();
1604        let actual: CodeInfoResponse = from_json(code_info).unwrap();
1605        assert_eq!(code_id, actual.code_id);
1606        assert_eq!(creator_addr.as_str(), actual.creator.as_str());
1607        assert_eq!(32, actual.checksum.as_slice().len());
1608    }
1609
1610    #[test]
1611    #[cfg(feature = "cosmwasm_1_2")]
1612    fn different_contracts_must_have_different_checksum() {
1613        let api = MockApi::default();
1614        let creator_addr = api.addr_make("creator");
1615        let wasm_storage = MockStorage::new();
1616        let mut wasm_keeper = wasm_keeper();
1617        let block = mock_env().block;
1618        let code_id_payout = wasm_keeper.store_code(creator_addr.clone(), payout::contract());
1619        let code_id_caller = wasm_keeper.store_code(creator_addr, caller::contract());
1620        let querier: MockQuerier<Empty> = MockQuerier::new(&[]);
1621        let query_payout = WasmQuery::CodeInfo {
1622            code_id: code_id_payout,
1623        };
1624        let query_caller = WasmQuery::CodeInfo {
1625            code_id: code_id_caller,
1626        };
1627        let code_info_payout = wasm_keeper
1628            .query(&api, &wasm_storage, &querier, &block, query_payout)
1629            .unwrap();
1630        let code_info_caller = wasm_keeper
1631            .query(&api, &wasm_storage, &querier, &block, query_caller)
1632            .unwrap();
1633        let info_payout: CodeInfoResponse = from_json(code_info_payout).unwrap();
1634        let info_caller: CodeInfoResponse = from_json(code_info_caller).unwrap();
1635        assert_eq!(code_id_payout, info_payout.code_id);
1636        assert_eq!(code_id_caller, info_caller.code_id);
1637        assert_ne!(info_caller.code_id, info_payout.code_id);
1638        assert_eq!(info_caller.creator, info_payout.creator);
1639        assert_ne!(info_caller.checksum, info_payout.checksum);
1640    }
1641
1642    #[test]
1643    #[cfg(feature = "cosmwasm_1_2")]
1644    fn querying_invalid_code_info_must_fail() {
1645        let api = MockApi::default();
1646        let wasm_storage = MockStorage::new();
1647        let wasm_keeper = wasm_keeper();
1648        let block = mock_env().block;
1649
1650        let querier: MockQuerier<Empty> = MockQuerier::new(&[]);
1651        let query = WasmQuery::CodeInfo { code_id: 100 };
1652
1653        wasm_keeper
1654            .query(&api, &wasm_storage, &querier, &block, query)
1655            .unwrap_err();
1656    }
1657
1658    #[test]
1659    fn can_dump_raw_wasm_state() {
1660        let api = MockApi::default();
1661
1662        // prepare user addresses
1663        let creator_addr = api.addr_make("creator");
1664        let admin_addr = api.addr_make("admin");
1665        let user_addr = api.addr_make("foobar");
1666
1667        let mut wasm_keeper = wasm_keeper();
1668        let block = mock_env().block;
1669        let code_id = wasm_keeper.store_code(creator_addr, payout::contract());
1670
1671        let mut wasm_storage = MockStorage::new();
1672
1673        let contract_addr = wasm_keeper
1674            .register_contract(
1675                &api,
1676                &mut wasm_storage,
1677                code_id,
1678                user_addr.clone(),
1679                admin_addr,
1680                "label".to_owned(),
1681                1000,
1682                None,
1683            )
1684            .unwrap();
1685
1686        // make a contract with state
1687        let payout = coin(1500, "mlg");
1688        let msg = payout::InstantiateMessage {
1689            payout: payout.clone(),
1690        };
1691        wasm_keeper
1692            .call_instantiate(
1693                contract_addr.clone(),
1694                &api,
1695                &mut wasm_storage,
1696                &mock_router(),
1697                &block,
1698                message_info(&user_addr, &[]),
1699                to_json_vec(&msg).unwrap(),
1700            )
1701            .unwrap();
1702
1703        // dump state
1704        let state = wasm_keeper.dump_wasm_raw(&wasm_storage, &contract_addr);
1705        assert_eq!(state.len(), 2);
1706        // check contents
1707        let (k, v) = &state[0];
1708        assert_eq!(k.as_slice(), b"count");
1709        let count: u32 = from_json(v).unwrap();
1710        assert_eq!(count, 1);
1711        let (k, v) = &state[1];
1712        assert_eq!(k.as_slice(), b"payout");
1713        let stored_pay: payout::InstantiateMessage = from_json(v).unwrap();
1714        assert_eq!(stored_pay.payout, payout);
1715    }
1716
1717    #[test]
1718    fn contract_send_coins() {
1719        let api = MockApi::default();
1720
1721        // prepare user addresses
1722        let creator_addr = api.addr_make("creator");
1723        let user_addr = api.addr_make("foobar");
1724
1725        let mut wasm_keeper = wasm_keeper();
1726        let block = mock_env().block;
1727        let code_id = wasm_keeper.store_code(creator_addr, payout::contract());
1728
1729        let mut wasm_storage = MockStorage::new();
1730        let mut cache = StorageTransaction::new(&wasm_storage);
1731
1732        let contract_addr = wasm_keeper
1733            .register_contract(
1734                &api,
1735                &mut cache,
1736                code_id,
1737                user_addr.clone(),
1738                None,
1739                "label".to_owned(),
1740                1000,
1741                None,
1742            )
1743            .unwrap();
1744
1745        let payout = coin(100, "TGD");
1746
1747        // init the contract
1748        let info = message_info(&user_addr, &[]);
1749        let init_msg = to_json_vec(&payout::InstantiateMessage {
1750            payout: payout.clone(),
1751        })
1752        .unwrap();
1753        let res = wasm_keeper
1754            .call_instantiate(
1755                contract_addr.clone(),
1756                &api,
1757                &mut cache,
1758                &mock_router(),
1759                &block,
1760                info,
1761                init_msg,
1762            )
1763            .unwrap();
1764        assert_eq!(0, res.messages.len());
1765
1766        // execute the contract
1767        let info = message_info(&user_addr, &[]);
1768        let res = wasm_keeper
1769            .call_execute(
1770                &api,
1771                &mut cache,
1772                contract_addr.clone(),
1773                &mock_router(),
1774                &block,
1775                info,
1776                b"{}".to_vec(),
1777            )
1778            .unwrap();
1779        assert_eq!(1, res.messages.len());
1780        match &res.messages[0].msg {
1781            CosmosMsg::Bank(BankMsg::Send { to_address, amount }) => {
1782                assert_eq!(to_address.as_str(), user_addr.as_str());
1783                assert_eq!(amount.as_slice(), &[payout.clone()]);
1784            }
1785            m => panic!("Unexpected message {m:?}"),
1786        }
1787
1788        // and flush before query
1789        cache.prepare().commit(&mut wasm_storage);
1790
1791        // query the contract
1792        let query = to_json_vec(&payout::QueryMsg::Payout {}).unwrap();
1793        let querier: MockQuerier<Empty> = MockQuerier::new(&[]);
1794        let data = wasm_keeper
1795            .query_smart(contract_addr, &api, &wasm_storage, &querier, &block, query)
1796            .unwrap();
1797        let res: payout::InstantiateMessage = from_json(data).unwrap();
1798        assert_eq!(res.payout, payout);
1799    }
1800
1801    fn assert_payout(
1802        router: &WasmKeeper<Empty, Empty>,
1803        storage: &mut dyn Storage,
1804        contract_addr: &Addr,
1805        payout: &Coin,
1806    ) {
1807        let api = MockApi::default();
1808        let user_addr = api.addr_make("silly");
1809        let info = message_info(&user_addr, &[]);
1810        let res = router
1811            .call_execute(
1812                &api,
1813                storage,
1814                contract_addr.clone(),
1815                &mock_router(),
1816                &mock_env().block,
1817                info,
1818                b"{}".to_vec(),
1819            )
1820            .unwrap();
1821        assert_eq!(1, res.messages.len());
1822        match &res.messages[0].msg {
1823            CosmosMsg::Bank(BankMsg::Send { to_address, amount }) => {
1824                assert_eq!(to_address.as_str(), user_addr.as_str());
1825                assert_eq!(amount.as_slice(), &[payout.clone()]);
1826            }
1827            m => panic!("Unexpected message {m:?}"),
1828        }
1829    }
1830
1831    fn assert_no_contract(storage: &dyn Storage, contract_addr: &Addr) {
1832        let contract = CONTRACTS.may_load(storage, contract_addr).unwrap();
1833        assert!(contract.is_none(), "{contract_addr:?}");
1834    }
1835
1836    #[test]
1837    fn multi_level_wasm_cache() {
1838        let api = MockApi::default();
1839
1840        // prepare user addresses
1841        let creator_addr = api.addr_make("creator");
1842        let user_addr = api.addr_make("foobar");
1843        let user_addr_1 = api.addr_make("johnny");
1844
1845        let mut wasm_keeper = wasm_keeper();
1846        let block = mock_env().block;
1847        let code_id = wasm_keeper.store_code(creator_addr, payout::contract());
1848
1849        let mut wasm_storage = MockStorage::new();
1850
1851        let payout1 = coin(100, "TGD");
1852
1853        // set contract 1 and commit (on router)
1854        let contract1 = transactional(&mut wasm_storage, |cache, _| {
1855            let contract = wasm_keeper
1856                .register_contract(
1857                    &api,
1858                    cache,
1859                    code_id,
1860                    user_addr.clone(),
1861                    None,
1862                    "".to_string(),
1863                    1000,
1864                    None,
1865                )
1866                .unwrap();
1867            let info = message_info(&user_addr, &[]);
1868            let init_msg = to_json_vec(&payout::InstantiateMessage {
1869                payout: payout1.clone(),
1870            })
1871            .unwrap();
1872            wasm_keeper
1873                .call_instantiate(
1874                    contract.clone(),
1875                    &api,
1876                    cache,
1877                    &mock_router(),
1878                    &block,
1879                    info,
1880                    init_msg,
1881                )
1882                .unwrap();
1883
1884            Ok(contract)
1885        })
1886        .unwrap();
1887
1888        let payout2 = coin(50, "BTC");
1889        let payout3 = coin(1234, "ATOM");
1890
1891        // create a new cache and check we can use contract 1
1892        let (contract2, contract3) = transactional(&mut wasm_storage, |cache, wasm_reader| {
1893            assert_payout(&wasm_keeper, cache, &contract1, &payout1);
1894
1895            // create contract 2 and use it
1896            let contract2 = wasm_keeper
1897                .register_contract(
1898                    &api,
1899                    cache,
1900                    code_id,
1901                    user_addr.clone(),
1902                    None,
1903                    "".to_owned(),
1904                    1000,
1905                    None,
1906                )
1907                .unwrap();
1908            let info = message_info(&user_addr, &[]);
1909            let init_msg = to_json_vec(&payout::InstantiateMessage {
1910                payout: payout2.clone(),
1911            })
1912            .unwrap();
1913            let _res = wasm_keeper
1914                .call_instantiate(
1915                    contract2.clone(),
1916                    &api,
1917                    cache,
1918                    &mock_router(),
1919                    &block,
1920                    info,
1921                    init_msg,
1922                )
1923                .unwrap();
1924            assert_payout(&wasm_keeper, cache, &contract2, &payout2);
1925
1926            // create a level2 cache and check we can use contract 1 and contract 2
1927            let contract3 = transactional(cache, |cache2, read| {
1928                assert_payout(&wasm_keeper, cache2, &contract1, &payout1);
1929                assert_payout(&wasm_keeper, cache2, &contract2, &payout2);
1930
1931                // create a contract on level 2
1932                let contract3 = wasm_keeper
1933                    .register_contract(
1934                        &api,
1935                        cache2,
1936                        code_id,
1937                        user_addr,
1938                        None,
1939                        "".to_owned(),
1940                        1000,
1941                        None,
1942                    )
1943                    .unwrap();
1944                let info = message_info(&user_addr_1, &[]);
1945                let init_msg = to_json_vec(&payout::InstantiateMessage {
1946                    payout: payout3.clone(),
1947                })
1948                .unwrap();
1949                let _res = wasm_keeper
1950                    .call_instantiate(
1951                        contract3.clone(),
1952                        &api,
1953                        cache2,
1954                        &mock_router(),
1955                        &block,
1956                        info,
1957                        init_msg,
1958                    )
1959                    .unwrap();
1960                assert_payout(&wasm_keeper, cache2, &contract3, &payout3);
1961
1962                // ensure first cache still doesn't see this contract
1963                assert_no_contract(read, &contract3);
1964                Ok(contract3)
1965            })
1966            .unwrap();
1967
1968            // after applying transaction, all contracts present on cache
1969            assert_payout(&wasm_keeper, cache, &contract1, &payout1);
1970            assert_payout(&wasm_keeper, cache, &contract2, &payout2);
1971            assert_payout(&wasm_keeper, cache, &contract3, &payout3);
1972
1973            // but not yet the root router
1974            assert_no_contract(wasm_reader, &contract1);
1975            assert_no_contract(wasm_reader, &contract2);
1976            assert_no_contract(wasm_reader, &contract3);
1977
1978            Ok((contract2, contract3))
1979        })
1980        .unwrap();
1981
1982        // ensure that it is now applied to the router
1983        assert_payout(&wasm_keeper, &mut wasm_storage, &contract1, &payout1);
1984        assert_payout(&wasm_keeper, &mut wasm_storage, &contract2, &payout2);
1985        assert_payout(&wasm_keeper, &mut wasm_storage, &contract3, &payout3);
1986    }
1987
1988    fn assert_admin(
1989        storage: &dyn Storage,
1990        wasm_keeper: &WasmKeeper<Empty, Empty>,
1991        contract_addr: &impl ToString,
1992        admin: Option<Addr>,
1993    ) {
1994        let api = MockApi::default();
1995        let querier: MockQuerier<Empty> = MockQuerier::new(&[]);
1996        // query
1997        let data = wasm_keeper
1998            .query(
1999                &api,
2000                storage,
2001                &querier,
2002                &mock_env().block,
2003                WasmQuery::ContractInfo {
2004                    contract_addr: contract_addr.to_string(),
2005                },
2006            )
2007            .unwrap();
2008        let res: ContractInfoResponse = from_json(data).unwrap();
2009        assert_eq!(res.admin, admin);
2010    }
2011
2012    #[test]
2013    fn update_clear_admin_works() {
2014        let api = MockApi::default();
2015        let mut wasm_keeper = wasm_keeper();
2016        let block = mock_env().block;
2017        let creator = api.addr_make("creator");
2018        let code_id = wasm_keeper.store_code(creator.clone(), caller::contract());
2019
2020        let mut wasm_storage = MockStorage::new();
2021
2022        let admin = api.addr_make("admin");
2023        let new_admin = api.addr_make("new_admin");
2024        let normal_user = api.addr_make("normal_user");
2025
2026        let contract_addr = wasm_keeper
2027            .register_contract(
2028                &api,
2029                &mut wasm_storage,
2030                code_id,
2031                creator,
2032                admin.clone(),
2033                "label".to_owned(),
2034                1000,
2035                None,
2036            )
2037            .unwrap();
2038
2039        // init the contract
2040        let info = message_info(&admin, &[]);
2041        let init_msg = to_json_vec(&Empty {}).unwrap();
2042        let res = wasm_keeper
2043            .call_instantiate(
2044                contract_addr.clone(),
2045                &api,
2046                &mut wasm_storage,
2047                &mock_router(),
2048                &block,
2049                info,
2050                init_msg,
2051            )
2052            .unwrap();
2053        assert_eq!(0, res.messages.len());
2054
2055        assert_admin(
2056            &wasm_storage,
2057            &wasm_keeper,
2058            &contract_addr,
2059            Some(admin.clone()),
2060        );
2061
2062        // non-admin should not be allowed to become admin on their own
2063        wasm_keeper
2064            .execute_wasm(
2065                &api,
2066                &mut wasm_storage,
2067                &mock_router(),
2068                &block,
2069                normal_user.clone(),
2070                WasmMsg::UpdateAdmin {
2071                    contract_addr: contract_addr.to_string(),
2072                    admin: normal_user.to_string(),
2073                },
2074            )
2075            .unwrap_err();
2076
2077        // should still be admin
2078        assert_admin(
2079            &wasm_storage,
2080            &wasm_keeper,
2081            &contract_addr,
2082            Some(admin.clone()),
2083        );
2084
2085        // admin should be allowed to transfer administration permissions
2086        let res = wasm_keeper
2087            .execute_wasm(
2088                &api,
2089                &mut wasm_storage,
2090                &mock_router(),
2091                &block,
2092                admin,
2093                WasmMsg::UpdateAdmin {
2094                    contract_addr: contract_addr.to_string(),
2095                    admin: new_admin.to_string(),
2096                },
2097            )
2098            .unwrap();
2099        assert_eq!(res.events.len(), 0);
2100
2101        // new_admin should now be admin
2102        assert_admin(
2103            &wasm_storage,
2104            &wasm_keeper,
2105            &contract_addr,
2106            Some(new_admin.clone()),
2107        );
2108
2109        // new_admin should now be able to clear to admin
2110        let res = wasm_keeper
2111            .execute_wasm(
2112                &api,
2113                &mut wasm_storage,
2114                &mock_router(),
2115                &block,
2116                new_admin,
2117                WasmMsg::ClearAdmin {
2118                    contract_addr: contract_addr.to_string(),
2119                },
2120            )
2121            .unwrap();
2122        assert_eq!(res.events.len(), 0);
2123
2124        // should have no admin now
2125        assert_admin(&wasm_storage, &wasm_keeper, &contract_addr, None);
2126    }
2127
2128    #[test]
2129    fn uses_simple_address_generator_by_default() {
2130        let api = MockApi::default();
2131        let mut wasm_keeper = wasm_keeper();
2132        let creator_addr = api.addr_make("creator");
2133        let code_id = wasm_keeper.store_code(creator_addr.clone(), payout::contract());
2134        assert_eq!(1, code_id);
2135
2136        let mut wasm_storage = MockStorage::new();
2137
2138        let admin = api.addr_make("admin");
2139        let contract_addr = wasm_keeper
2140            .register_contract(
2141                &api,
2142                &mut wasm_storage,
2143                code_id,
2144                creator_addr.clone(),
2145                admin.clone(),
2146                "label".to_owned(),
2147                1000,
2148                None,
2149            )
2150            .unwrap();
2151
2152        assert_eq!(
2153            contract_addr.as_str(),
2154            "cosmwasm1mzdhwvvh22wrt07w59wxyd58822qavwkx5lcej7aqfkpqqlhaqfsgn6fq2",
2155            "default address generator returned incorrect address"
2156        );
2157
2158        let salt = HexBinary::from_hex("c0ffee").unwrap();
2159
2160        let contract_addr = wasm_keeper
2161            .register_contract(
2162                &api,
2163                &mut wasm_storage,
2164                code_id,
2165                creator_addr.clone(),
2166                admin.clone(),
2167                "label".to_owned(),
2168                1000,
2169                Binary::from(salt.clone()),
2170            )
2171            .unwrap();
2172
2173        assert_eq!(
2174            contract_addr.as_str(),
2175            "cosmwasm1drhu6t78wacgm5qjzs4hvkv9fd9awa9henw7fh6vmzrhf7k2nkjsg3flns",
2176            "default address generator returned incorrect address"
2177        );
2178
2179        let code_id = wasm_keeper.store_code(creator_addr, payout::contract());
2180        assert_eq!(2, code_id);
2181
2182        let user_addr = api.addr_make("boobaz");
2183
2184        let contract_addr = wasm_keeper
2185            .register_contract(
2186                &api,
2187                &mut wasm_storage,
2188                code_id,
2189                user_addr,
2190                admin,
2191                "label".to_owned(),
2192                1000,
2193                Binary::from(salt),
2194            )
2195            .unwrap();
2196
2197        assert_eq!(
2198            contract_addr.as_str(),
2199            "cosmwasm13cfeertf2gny0rzp5jwqzst8crmfgvcd2lq5su0c9z66yxa45qdsdd0uxc",
2200            "default address generator returned incorrect address"
2201        );
2202    }
2203
2204    struct TestAddressGenerator {
2205        address: Addr,
2206        predictable_address: Addr,
2207    }
2208
2209    impl AddressGenerator for TestAddressGenerator {
2210        fn contract_address(
2211            &self,
2212            _api: &dyn Api,
2213            _storage: &mut dyn Storage,
2214            _code_id: u64,
2215            _instance_id: u64,
2216        ) -> AnyResult<Addr> {
2217            Ok(self.address.clone())
2218        }
2219
2220        fn predictable_contract_address(
2221            &self,
2222            _api: &dyn Api,
2223            _storage: &mut dyn Storage,
2224            _code_id: u64,
2225            _instance_id: u64,
2226            _checksum: &[u8],
2227            _creator: &CanonicalAddr,
2228            _salt: &[u8],
2229        ) -> AnyResult<Addr> {
2230            Ok(self.predictable_address.clone())
2231        }
2232    }
2233
2234    #[test]
2235    fn can_use_custom_address_generator() {
2236        let api = MockApi::default();
2237        let expected_addr = api.addr_make("address");
2238        let expected_predictable_addr = api.addr_make("predictable_address");
2239        let mut wasm_keeper: WasmKeeper<Empty, Empty> =
2240            WasmKeeper::new().with_address_generator(TestAddressGenerator {
2241                address: expected_addr.clone(),
2242                predictable_address: expected_predictable_addr.clone(),
2243            });
2244        let creator = api.addr_make("creator");
2245        let code_id = wasm_keeper.store_code(creator.clone(), payout::contract());
2246
2247        let mut wasm_storage = MockStorage::new();
2248
2249        let admin = api.addr_make("admin");
2250        let contract_addr = wasm_keeper
2251            .register_contract(
2252                &api,
2253                &mut wasm_storage,
2254                code_id,
2255                creator.clone(),
2256                admin.clone(),
2257                "label".to_owned(),
2258                1000,
2259                None,
2260            )
2261            .unwrap();
2262
2263        assert_eq!(
2264            contract_addr, expected_addr,
2265            "custom address generator returned incorrect address"
2266        );
2267
2268        let contract_addr = wasm_keeper
2269            .register_contract(
2270                &api,
2271                &mut wasm_storage,
2272                code_id,
2273                creator,
2274                admin,
2275                "label".to_owned(),
2276                1000,
2277                Binary::from(HexBinary::from_hex("23A74B8C").unwrap()),
2278            )
2279            .unwrap();
2280
2281        assert_eq!(
2282            contract_addr, expected_predictable_addr,
2283            "custom address generator returned incorrect address"
2284        );
2285    }
2286}