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