1use std::cell::RefCell;
2use std::fmt::{self, Debug};
3use std::marker::PhantomData;
4use std::ops::{Deref, DerefMut};
5
6use anyhow::bail;
7use anyhow::Result as AnyResult;
8use cosmwasm_std::testing::{mock_env, MockApi, MockStorage};
9use cosmwasm_std::{
10 from_binary, from_slice, to_binary, Addr, AllBalanceResponse, Api, BalanceResponse, BankQuery,
11 Binary, BlockInfo, ContractInfoResponse, ContractResult, CosmosMsg, CustomQuery, Empty, GovMsg,
12 IbcMsg, IbcQuery, Querier, QuerierResult, QuerierWrapper, QueryRequest, Record, Storage,
13 SupplyResponse, SystemError, SystemResult, WasmQuery,
14};
15use osmosis_std::types::cosmos::bank::v1beta1::{
16 QueryAllBalancesRequest, QueryAllBalancesResponse, QueryBalanceRequest, QueryBalanceResponse,
17 QuerySupplyOfRequest, QuerySupplyOfResponse,
18};
19use osmosis_std::types::cosmwasm::wasm::v1::{
20 ContractInfo, QueryContractInfoRequest, QueryContractInfoResponse,
21 QuerySmartContractStateRequest, QuerySmartContractStateResponse,
22};
23use schemars::JsonSchema;
24use serde::de::DeserializeOwned;
25use serde::Serialize;
26
27use crate::bank::{Bank, BankKeeper, BankSudo};
28use crate::contracts::Contract;
29use crate::executor::{AppResponse, Executor};
30use crate::gov::Gov;
31use crate::ibc::Ibc;
32use crate::module::{FailingModule, Module};
33use crate::staking::{Distribution, DistributionKeeper, StakeKeeper, Staking, StakingSudo};
34use crate::stargate::{Stargate, StargateKeeper, StargateMsg};
35use crate::transactions::transactional;
36use crate::wasm::{ContractData, Wasm, WasmKeeper, WasmSudo};
37use crate::{
38 QUERY_ALL_BALANCES_PATH, QUERY_BALANCE_PATH, QUERY_SUPPLY_PATH, QUERY_WASM_CONTRACT_INFO_PATH,
39 QUERY_WASM_CONTRACT_SMART_PATH,
40};
41
42pub fn next_block(block: &mut BlockInfo) {
43 block.time = block.time.plus_seconds(5);
44 block.height += 1;
45}
46
47pub type BasicApp<ExecC = Empty, QueryC = Empty> = App<
49 BankKeeper,
50 MockApi,
51 MockStorage,
52 FailingModule<ExecC, QueryC, Empty>,
53 WasmKeeper<ExecC, QueryC>,
54 StakeKeeper,
55 DistributionKeeper,
56 FailingModule<IbcMsg, IbcQuery, Empty>,
57 FailingModule<GovMsg, Empty, Empty>,
58 StargateKeeper<ExecC, QueryC>,
59>;
60
61pub struct App<
65 Bank = BankKeeper,
66 Api = MockApi,
67 Storage = MockStorage,
68 Custom = FailingModule<Empty, Empty, Empty>,
69 Wasm = WasmKeeper<Empty, Empty>,
70 Staking = StakeKeeper,
71 Distr = DistributionKeeper,
72 Ibc = FailingModule<IbcMsg, IbcQuery, Empty>,
73 Gov = FailingModule<GovMsg, Empty, Empty>,
74 Stargate = StargateKeeper<Empty, Empty>,
75> {
76 pub router: RefCell<Router<Bank, Custom, Wasm, Staking, Distr, Ibc, Gov, Stargate>>,
77 api: Api,
78 storage: RefCell<Storage>,
79 block: RefCell<BlockInfo>,
80}
81
82fn no_init<BankT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>(
83 _: &mut Router<BankT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>,
84 _: &dyn Api,
85 _: &mut dyn Storage,
86) {
87}
88
89impl Default for App {
90 fn default() -> Self {
91 Self::new(no_init)
92 }
93}
94
95impl BasicApp {
96 pub fn new<F>(init_fn: F) -> Self
98 where
99 F: FnOnce(
100 &mut Router<
101 BankKeeper,
102 FailingModule<Empty, Empty, Empty>,
103 WasmKeeper<Empty, Empty>,
104 StakeKeeper,
105 DistributionKeeper,
106 FailingModule<IbcMsg, IbcQuery, Empty>,
107 FailingModule<GovMsg, Empty, Empty>,
108 StargateKeeper<Empty, Empty>,
109 >,
110 &dyn Api,
111 &mut dyn Storage,
112 ),
113 {
114 AppBuilder::new().build(init_fn)
115 }
116}
117
118pub fn custom_app<ExecC, QueryC, F>(init_fn: F) -> BasicApp<ExecC, QueryC>
121where
122 ExecC: Debug + Clone + PartialEq + JsonSchema + DeserializeOwned + 'static,
123 QueryC: Debug + CustomQuery + DeserializeOwned + 'static,
124 F: FnOnce(
125 &mut Router<
126 BankKeeper,
127 FailingModule<ExecC, QueryC, Empty>,
128 WasmKeeper<ExecC, QueryC>,
129 StakeKeeper,
130 DistributionKeeper,
131 FailingModule<IbcMsg, IbcQuery, Empty>,
132 FailingModule<GovMsg, Empty, Empty>,
133 StargateKeeper<ExecC, QueryC>,
134 >,
135 &dyn Api,
136 &mut dyn Storage,
137 ),
138{
139 AppBuilder::new_custom().build(init_fn)
140}
141
142impl<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT> Querier
143 for App<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
144where
145 CustomT::ExecT: Clone + fmt::Debug + PartialEq + JsonSchema + DeserializeOwned + 'static,
146 CustomT::QueryT: CustomQuery + DeserializeOwned + 'static,
147 WasmT: Wasm<CustomT::ExecT, CustomT::QueryT>,
148 BankT: Bank,
149 ApiT: Api,
150 StorageT: Storage,
151 CustomT: Module,
152 StakingT: Staking,
153 DistrT: Distribution,
154 IbcT: Ibc,
155 GovT: Gov,
156 StargateT: Stargate<CustomT::ExecT, CustomT::QueryT>,
157{
158 fn raw_query(&self, bin_request: &[u8]) -> QuerierResult {
159 self.router
160 .borrow()
161 .querier(
162 &self.api,
163 self.storage.borrow().deref(),
164 &self.block.borrow(),
165 )
166 .raw_query(bin_request)
167 }
168}
169
170impl<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
171 Executor<CustomT::ExecT>
172 for App<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
173where
174 CustomT::ExecT: Clone + fmt::Debug + PartialEq + JsonSchema + DeserializeOwned + 'static,
175 CustomT::QueryT: CustomQuery + DeserializeOwned + 'static,
176 WasmT: Wasm<CustomT::ExecT, CustomT::QueryT>,
177 BankT: Bank,
178 ApiT: Api,
179 StorageT: Storage,
180 CustomT: Module,
181 StakingT: Staking,
182 DistrT: Distribution,
183 IbcT: Ibc,
184 GovT: Gov,
185 StargateT: Stargate<CustomT::ExecT, CustomT::QueryT>,
186{
187 fn execute(
188 &self,
189 sender: Addr,
190 msg: cosmwasm_std::CosmosMsg<CustomT::ExecT>,
191 ) -> AnyResult<AppResponse> {
192 let mut all = self.execute_multi(sender, vec![msg])?;
193 let res = all.pop().unwrap();
194 Ok(res)
195 }
196}
197
198pub type BasicAppBuilder<ExecC, QueryC> = AppBuilder<
201 BankKeeper,
202 MockApi,
203 MockStorage,
204 FailingModule<ExecC, QueryC, Empty>,
205 WasmKeeper<ExecC, QueryC>,
206 StakeKeeper,
207 DistributionKeeper,
208 FailingModule<IbcMsg, IbcQuery, Empty>,
209 FailingModule<GovMsg, Empty, Empty>,
210 StargateKeeper<ExecC, QueryC>,
211>;
212
213pub struct AppBuilder<Bank, Api, Storage, Custom, Wasm, Staking, Distr, Ibc, Gov, Stargate> {
215 api: Api,
216 block: BlockInfo,
217 storage: Storage,
218 bank: Bank,
219 wasm: Wasm,
220 custom: Custom,
221 staking: Staking,
222 distribution: Distr,
223 ibc: Ibc,
224 gov: Gov,
225 stargate: Stargate,
226}
227
228impl Default
229 for AppBuilder<
230 BankKeeper,
231 MockApi,
232 MockStorage,
233 FailingModule<Empty, Empty, Empty>,
234 WasmKeeper<Empty, Empty>,
235 StakeKeeper,
236 DistributionKeeper,
237 FailingModule<IbcMsg, IbcQuery, Empty>,
238 FailingModule<GovMsg, Empty, Empty>,
239 StargateKeeper<Empty, Empty>,
240 >
241{
242 fn default() -> Self {
243 Self::new()
244 }
245}
246
247impl
248 AppBuilder<
249 BankKeeper,
250 MockApi,
251 MockStorage,
252 FailingModule<Empty, Empty, Empty>,
253 WasmKeeper<Empty, Empty>,
254 StakeKeeper,
255 DistributionKeeper,
256 FailingModule<IbcMsg, IbcQuery, Empty>,
257 FailingModule<GovMsg, Empty, Empty>,
258 StargateKeeper<Empty, Empty>,
259 >
260{
261 pub fn new() -> Self {
263 AppBuilder {
264 api: MockApi::default(),
265 block: mock_env().block,
266 storage: MockStorage::new(),
267 bank: BankKeeper::new(),
268 wasm: WasmKeeper::new(),
269 custom: FailingModule::new(),
270 staking: StakeKeeper::new(),
271 distribution: DistributionKeeper::new(),
272 ibc: FailingModule::new(),
273 gov: FailingModule::new(),
274 stargate: StargateKeeper::new(),
275 }
276 }
277}
278
279impl<ExecC, QueryC>
280 AppBuilder<
281 BankKeeper,
282 MockApi,
283 MockStorage,
284 FailingModule<ExecC, QueryC, Empty>,
285 WasmKeeper<ExecC, QueryC>,
286 StakeKeeper,
287 DistributionKeeper,
288 FailingModule<IbcMsg, IbcQuery, Empty>,
289 FailingModule<GovMsg, Empty, Empty>,
290 StargateKeeper<ExecC, QueryC>,
291 >
292where
293 ExecC: Debug + Clone + PartialEq + JsonSchema + DeserializeOwned + 'static,
294 QueryC: Debug + CustomQuery + DeserializeOwned + 'static,
295{
296 pub fn new_custom() -> Self {
299 AppBuilder {
300 api: MockApi::default(),
301 block: mock_env().block,
302 storage: MockStorage::new(),
303 bank: BankKeeper::new(),
304 wasm: WasmKeeper::new(),
305 custom: FailingModule::new(),
306 staking: StakeKeeper::new(),
307 distribution: DistributionKeeper::new(),
308 ibc: FailingModule::new(),
309 gov: FailingModule::new(),
310 stargate: StargateKeeper::new(),
311 }
312 }
313}
314
315impl<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
316 AppBuilder<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
317{
318 pub fn construct(
319 api: ApiT,
320 block: BlockInfo,
321 storage: StorageT,
322 bank: BankT,
323 wasm: WasmT,
324 custom: CustomT,
325 staking: StakingT,
326 distribution: DistrT,
327 ibc: IbcT,
328 gov: GovT,
329 stargate: StargateT,
330 ) -> Self {
331 Self {
332 api,
333 block,
334 storage,
335 bank,
336 wasm,
337 custom,
338 staking,
339 distribution,
340 ibc,
341 gov,
342 stargate,
343 }
344 }
345
346 pub fn with_wasm<ExecC, QueryC: CustomQuery, NewWasm: Wasm<ExecC, QueryC>>(
356 self,
357 wasm: NewWasm,
358 ) -> AppBuilder<BankT, ApiT, StorageT, CustomT, NewWasm, StakingT, DistrT, IbcT, GovT, StargateT>
359 {
360 let AppBuilder {
361 bank,
362 api,
363 storage,
364 custom,
365 block,
366 staking,
367 distribution,
368 ibc,
369 gov,
370 stargate,
371 ..
372 } = self;
373
374 AppBuilder {
375 api,
376 block,
377 storage,
378 bank,
379 wasm,
380 custom,
381 staking,
382 distribution,
383 ibc,
384 gov,
385 stargate,
386 }
387 }
388
389 pub fn with_stargate(
390 self,
391 stargate: StargateT,
392 ) -> AppBuilder<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
393 {
394 let AppBuilder {
395 bank,
396 api,
397 storage,
398 custom,
399 block,
400 wasm,
401 staking,
402 distribution,
403 ibc,
404 gov,
405 ..
406 } = self;
407
408 AppBuilder {
409 api,
410 block,
411 storage,
412 bank,
413 wasm,
414 custom,
415 staking,
416 distribution,
417 ibc,
418 gov,
419 stargate,
420 }
421 }
422
423 pub fn with_bank<NewBank: Bank>(
425 self,
426 bank: NewBank,
427 ) -> AppBuilder<NewBank, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
428 {
429 let AppBuilder {
430 wasm,
431 api,
432 storage,
433 custom,
434 block,
435 staking,
436 distribution,
437 ibc,
438 gov,
439 stargate,
440 ..
441 } = self;
442
443 AppBuilder {
444 api,
445 block,
446 storage,
447 bank,
448 wasm,
449 custom,
450 staking,
451 distribution,
452 ibc,
453 gov,
454 stargate,
455 }
456 }
457
458 pub fn with_api<NewApi: Api>(
460 self,
461 api: NewApi,
462 ) -> AppBuilder<BankT, NewApi, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
463 {
464 let AppBuilder {
465 wasm,
466 bank,
467 storage,
468 custom,
469 block,
470 staking,
471 distribution,
472 ibc,
473 gov,
474 stargate,
475 ..
476 } = self;
477
478 AppBuilder {
479 api,
480 block,
481 storage,
482 bank,
483 wasm,
484 custom,
485 staking,
486 distribution,
487 ibc,
488 gov,
489 stargate,
490 }
491 }
492
493 pub fn with_storage<NewStorage: Storage>(
495 self,
496 storage: NewStorage,
497 ) -> AppBuilder<BankT, ApiT, NewStorage, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
498 {
499 let AppBuilder {
500 wasm,
501 api,
502 bank,
503 custom,
504 block,
505 staking,
506 distribution,
507 ibc,
508 gov,
509 stargate,
510 ..
511 } = self;
512
513 AppBuilder {
514 api,
515 block,
516 storage,
517 bank,
518 wasm,
519 custom,
520 staking,
521 distribution,
522 ibc,
523 gov,
524 stargate,
525 }
526 }
527
528 pub fn with_custom<NewCustom: Module>(
538 self,
539 custom: NewCustom,
540 ) -> AppBuilder<BankT, ApiT, StorageT, NewCustom, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
541 {
542 let AppBuilder {
543 wasm,
544 bank,
545 api,
546 storage,
547 block,
548 staking,
549 distribution,
550 ibc,
551 gov,
552 stargate,
553 ..
554 } = self;
555
556 AppBuilder {
557 api,
558 block,
559 storage,
560 bank,
561 wasm,
562 custom,
563 staking,
564 distribution,
565 ibc,
566 gov,
567 stargate,
568 }
569 }
570
571 pub fn with_staking<NewStaking: Staking>(
573 self,
574 staking: NewStaking,
575 ) -> AppBuilder<BankT, ApiT, StorageT, CustomT, WasmT, NewStaking, DistrT, IbcT, GovT, StargateT>
576 {
577 let AppBuilder {
578 wasm,
579 api,
580 storage,
581 custom,
582 block,
583 bank,
584 distribution,
585 ibc,
586 gov,
587 stargate,
588 ..
589 } = self;
590
591 AppBuilder {
592 api,
593 block,
594 storage,
595 bank,
596 wasm,
597 custom,
598 staking,
599 distribution,
600 ibc,
601 gov,
602 stargate,
603 }
604 }
605
606 pub fn with_distribution<NewDistribution: Distribution>(
608 self,
609 distribution: NewDistribution,
610 ) -> AppBuilder<
611 BankT,
612 ApiT,
613 StorageT,
614 CustomT,
615 WasmT,
616 StakingT,
617 NewDistribution,
618 IbcT,
619 GovT,
620 StargateT,
621 > {
622 let AppBuilder {
623 wasm,
624 api,
625 storage,
626 custom,
627 block,
628 staking,
629 bank,
630 ibc,
631 gov,
632 stargate,
633 ..
634 } = self;
635
636 AppBuilder {
637 api,
638 block,
639 storage,
640 bank,
641 wasm,
642 custom,
643 staking,
644 distribution,
645 ibc,
646 gov,
647 stargate,
648 }
649 }
650
651 pub fn with_ibc<NewIbc: Ibc>(
653 self,
654 ibc: NewIbc,
655 ) -> AppBuilder<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, NewIbc, GovT, StargateT>
656 {
657 let AppBuilder {
658 wasm,
659 api,
660 storage,
661 custom,
662 block,
663 staking,
664 bank,
665 distribution,
666 gov,
667 stargate,
668 ..
669 } = self;
670
671 AppBuilder {
672 api,
673 block,
674 storage,
675 bank,
676 wasm,
677 custom,
678 staking,
679 distribution,
680 ibc,
681 gov,
682 stargate,
683 }
684 }
685
686 pub fn with_gov<NewGov: Gov>(
688 self,
689 gov: NewGov,
690 ) -> AppBuilder<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, NewGov, StargateT>
691 {
692 let AppBuilder {
693 wasm,
694 api,
695 storage,
696 custom,
697 block,
698 staking,
699 bank,
700 distribution,
701 ibc,
702 stargate,
703 ..
704 } = self;
705
706 AppBuilder {
707 api,
708 block,
709 storage,
710 bank,
711 wasm,
712 custom,
713 staking,
714 distribution,
715 ibc,
716 gov,
717 stargate,
718 }
719 }
720
721 pub fn with_block(mut self, block: BlockInfo) -> Self {
723 self.block = block;
724 self
725 }
726
727 pub fn build<F>(
731 self,
732 init_fn: F,
733 ) -> App<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
734 where
735 BankT: Bank,
736 ApiT: Api,
737 StorageT: Storage,
738 CustomT: Module,
739 WasmT: Wasm<CustomT::ExecT, CustomT::QueryT>,
740 StakingT: Staking,
741 DistrT: Distribution,
742 IbcT: Ibc,
743 GovT: Gov,
744 F: FnOnce(
745 &mut Router<BankT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>,
746 &dyn Api,
747 &mut dyn Storage,
748 ),
749 {
750 let router = Router {
751 wasm: self.wasm,
752 bank: self.bank,
753 custom: self.custom,
754 staking: self.staking,
755 distribution: self.distribution,
756 ibc: self.ibc,
757 gov: self.gov,
758 stargate: self.stargate,
759 };
760
761 let app = App {
762 router: RefCell::new(router),
763 api: self.api,
764 block: RefCell::new(self.block),
765 storage: RefCell::new(self.storage),
766 };
767 app.init_modules(init_fn);
768 app
769 }
770}
771
772impl<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
773 App<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
774where
775 WasmT: Wasm<CustomT::ExecT, CustomT::QueryT>,
776 BankT: Bank,
777 ApiT: Api,
778 StorageT: Storage,
779 CustomT: Module,
780 StakingT: Staking,
781 DistrT: Distribution,
782 IbcT: Ibc,
783 GovT: Gov,
784{
785 pub fn init_modules<F, T>(&self, init_fn: F) -> T
786 where
787 F: FnOnce(
788 &mut Router<BankT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>,
789 &dyn Api,
790 &mut dyn Storage,
791 ) -> T,
792 {
793 init_fn(
794 self.router.borrow_mut().deref_mut(),
795 &self.api,
796 self.storage.borrow_mut().deref_mut(),
797 )
798 }
799
800 pub fn read_module<F, T>(&self, query_fn: F) -> T
801 where
802 F: FnOnce(
803 &Router<BankT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>,
804 &dyn Api,
805 &dyn Storage,
806 ) -> T,
807 {
808 query_fn(
809 &self.router.borrow(),
810 &self.api,
811 self.storage.borrow().deref(),
812 )
813 }
814}
815
816impl<BankT, ApiT, StorageT, CustomT, StakingT, DistrT, IbcT, GovT, StargateT>
819 App<
820 BankT,
821 ApiT,
822 StorageT,
823 CustomT,
824 WasmKeeper<CustomT::ExecT, CustomT::QueryT>,
825 StakingT,
826 DistrT,
827 IbcT,
828 GovT,
829 StargateT,
830 >
831where
832 BankT: Bank,
833 ApiT: Api,
834 StorageT: Storage,
835 CustomT: Module,
836 StakingT: Staking,
837 DistrT: Distribution,
838 IbcT: Ibc,
839 GovT: Gov,
840 CustomT::ExecT: Clone + fmt::Debug + PartialEq + JsonSchema + DeserializeOwned + 'static,
841 CustomT::QueryT: CustomQuery + DeserializeOwned + 'static,
842 StargateT: Stargate<CustomT::ExecT, CustomT::QueryT>,
843{
844 pub fn store_code(&self, code: Box<dyn Contract<CustomT::ExecT, CustomT::QueryT>>) -> u64 {
847 self.init_modules(|router, _, _| router.wasm.store_code(code) as u64)
848 }
849
850 pub fn contract_data(&self, address: &Addr) -> AnyResult<ContractData> {
852 self.read_module(|router, _, storage| router.wasm.load_contract(storage, address))
853 }
854
855 pub fn dump_wasm_raw(&self, address: &Addr) -> Vec<Record> {
857 self.read_module(|router, _, storage| router.wasm.dump_wasm_raw(storage, address))
858 }
859}
860
861impl<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
862 App<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
863where
864 CustomT::ExecT: std::fmt::Debug + PartialEq + Clone + JsonSchema + DeserializeOwned + 'static,
865 CustomT::QueryT: CustomQuery + DeserializeOwned + 'static,
866 WasmT: Wasm<CustomT::ExecT, CustomT::QueryT>,
867 BankT: Bank,
868 ApiT: Api,
869 StorageT: Storage,
870 CustomT: Module,
871 StakingT: Staking,
872 DistrT: Distribution,
873 IbcT: Ibc,
874 GovT: Gov,
875 StargateT: Stargate<CustomT::ExecT, CustomT::QueryT>,
876{
877 pub fn set_block(&self, block: BlockInfo) {
878 self.block.replace(block);
879 }
880
881 pub fn update_block<F: Fn(&mut BlockInfo)>(&self, action: F) {
883 action(self.block.borrow_mut().deref_mut());
884 }
886
887 pub fn block_info(&self) -> BlockInfo {
889 self.block.borrow().clone()
890 }
891
892 pub fn wrap(&self) -> QuerierWrapper<CustomT::QueryT> {
895 QuerierWrapper::new(self)
896 }
897
898 pub fn execute_multi(
902 &self,
903 sender: Addr,
904 msgs: Vec<cosmwasm_std::CosmosMsg<CustomT::ExecT>>,
905 ) -> AnyResult<Vec<AppResponse>> {
906 let Self {
911 block,
912 router,
913 api,
914 storage,
915 } = self;
916
917 transactional(storage.borrow_mut().deref_mut(), |write_cache, _| {
918 msgs.into_iter()
919 .map(|msg| {
920 router.borrow().execute(
921 api,
922 write_cache,
923 block.borrow().deref(),
924 sender.clone(),
925 msg,
926 )
927 })
928 .collect()
929 })
930 }
931
932 pub fn wasm_sudo<T: Serialize, U: Into<Addr>>(
936 &self,
937 contract_addr: U,
938 msg: &T,
939 ) -> AnyResult<AppResponse> {
940 let msg = to_binary(msg)?;
941
942 let Self {
943 block,
944 router,
945 api,
946 storage,
947 } = self;
948
949 transactional(storage.borrow_mut().deref_mut(), |write_cache, _| {
950 router.borrow().wasm.sudo(
951 api,
952 contract_addr.into(),
953 write_cache,
954 router.borrow().deref(),
955 block.borrow().deref(),
956 msg,
957 )
958 })
959 }
960
961 pub fn sudo(&self, msg: SudoMsg) -> AnyResult<AppResponse> {
965 let Self {
969 block,
970 router,
971 api,
972 storage,
973 } = self;
974
975 transactional(storage.borrow_mut().deref_mut(), |write_cache, _| {
976 router
977 .borrow()
978 .sudo(api, write_cache, block.borrow().deref(), msg)
979 })
980 }
981}
982
983pub struct Router<Bank, Custom, Wasm, Staking, Distr, Ibc, Gov, Stargate> {
984 pub wasm: Wasm,
987 pub bank: Bank,
989 pub custom: Custom,
990 pub staking: Staking,
991 pub distribution: Distr,
992 pub ibc: Ibc,
993 pub gov: Gov,
994 pub stargate: Stargate,
996}
997
998impl<BankT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
999 Router<BankT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
1000where
1001 CustomT::ExecT: Clone + fmt::Debug + PartialEq + JsonSchema + DeserializeOwned + 'static,
1002 CustomT::QueryT: CustomQuery + DeserializeOwned + 'static,
1003 CustomT: Module,
1004 WasmT: Wasm<CustomT::ExecT, CustomT::QueryT>,
1005 BankT: Bank,
1006 StakingT: Staking,
1007 DistrT: Distribution,
1008 IbcT: Ibc,
1009 GovT: Gov,
1010 StargateT: Stargate<CustomT::ExecT, CustomT::QueryT>,
1011{
1012 pub fn querier<'a>(
1013 &'a self,
1014 api: &'a dyn Api,
1015 storage: &'a dyn Storage,
1016 block_info: &'a BlockInfo,
1017 ) -> RouterQuerier<'a, CustomT::ExecT, CustomT::QueryT> {
1018 RouterQuerier {
1019 router: self,
1020 api,
1021 storage,
1022 block_info,
1023 }
1024 }
1025}
1026
1027pub enum SudoMsg {
1030 Bank(BankSudo),
1031 Custom(Empty),
1032 Staking(StakingSudo),
1033 Wasm(WasmSudo),
1034}
1035
1036impl From<WasmSudo> for SudoMsg {
1037 fn from(wasm: WasmSudo) -> Self {
1038 SudoMsg::Wasm(wasm)
1039 }
1040}
1041
1042impl From<BankSudo> for SudoMsg {
1043 fn from(bank: BankSudo) -> Self {
1044 SudoMsg::Bank(bank)
1045 }
1046}
1047
1048impl From<StakingSudo> for SudoMsg {
1049 fn from(staking: StakingSudo) -> Self {
1050 SudoMsg::Staking(staking)
1051 }
1052}
1053
1054pub trait CosmosRouter {
1055 type ExecC;
1056 type QueryC: CustomQuery;
1057
1058 fn execute(
1059 &self,
1060 api: &dyn Api,
1061 storage: &mut dyn Storage,
1062 block: &BlockInfo,
1063 sender: Addr,
1064 msg: CosmosMsg<Self::ExecC>,
1065 ) -> AnyResult<AppResponse>;
1066
1067 fn query(
1068 &self,
1069 api: &dyn Api,
1070 storage: &dyn Storage,
1071 block: &BlockInfo,
1072 request: QueryRequest<Self::QueryC>,
1073 ) -> AnyResult<Binary>;
1074
1075 fn sudo(
1076 &self,
1077 api: &dyn Api,
1078 storage: &mut dyn Storage,
1079 block: &BlockInfo,
1080 msg: SudoMsg,
1081 ) -> AnyResult<AppResponse>;
1082}
1083
1084impl<BankT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT> CosmosRouter
1085 for Router<BankT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
1086where
1087 CustomT::ExecT: std::fmt::Debug + Clone + PartialEq + JsonSchema + DeserializeOwned + 'static,
1088 CustomT::QueryT: CustomQuery + DeserializeOwned + 'static,
1089 CustomT: Module,
1090 WasmT: Wasm<CustomT::ExecT, CustomT::QueryT>,
1091 BankT: Bank,
1092 StakingT: Staking,
1093 DistrT: Distribution,
1094 IbcT: Ibc,
1095 GovT: Gov,
1096 StargateT: Stargate<CustomT::ExecT, CustomT::QueryT>,
1097{
1098 type ExecC = CustomT::ExecT;
1099 type QueryC = CustomT::QueryT;
1100
1101 fn execute(
1102 &self,
1103 api: &dyn Api,
1104 storage: &mut dyn Storage,
1105 block: &BlockInfo,
1106 sender: Addr,
1107 msg: CosmosMsg<Self::ExecC>,
1108 ) -> AnyResult<AppResponse> {
1109 match msg {
1110 CosmosMsg::Wasm(msg) => self.wasm.execute(api, storage, self, block, sender, msg),
1111 CosmosMsg::Bank(msg) => self.bank.execute(api, storage, self, block, sender, msg),
1112 CosmosMsg::Custom(msg) => self.custom.execute(api, storage, self, block, sender, msg),
1113 CosmosMsg::Staking(msg) => self.staking.execute(api, storage, self, block, sender, msg),
1114 CosmosMsg::Distribution(msg) => self
1115 .distribution
1116 .execute(api, storage, self, block, sender, msg),
1117 CosmosMsg::Ibc(msg) => self.ibc.execute(api, storage, self, block, sender, msg),
1118 CosmosMsg::Gov(msg) => self.gov.execute(api, storage, self, block, sender, msg),
1119 CosmosMsg::Stargate { type_url, value } => self.stargate.execute(
1120 api,
1121 storage,
1122 self,
1123 block,
1124 sender,
1125 StargateMsg { type_url, value },
1126 ),
1127 _ => bail!("Cannot execute {:?}", msg),
1128 }
1129 }
1130
1131 fn query(
1135 &self,
1136 api: &dyn Api,
1137 storage: &dyn Storage,
1138 block: &BlockInfo,
1139 request: QueryRequest<Self::QueryC>,
1140 ) -> AnyResult<Binary> {
1141 let querier = self.querier(api, storage, block);
1142 match request {
1143 QueryRequest::Wasm(req) => self.wasm.query(api, storage, &querier, block, req),
1144 QueryRequest::Bank(req) => self.bank.query(api, storage, &querier, block, req),
1145 QueryRequest::Custom(req) => self.custom.query(api, storage, &querier, block, req),
1146 QueryRequest::Staking(req) => self.staking.query(api, storage, &querier, block, req),
1147 QueryRequest::Ibc(req) => self.ibc.query(api, storage, &querier, block, req),
1148 QueryRequest::Stargate { path, data } => {
1149 match path.as_str() {
1160 QUERY_ALL_BALANCES_PATH => {
1162 let msg: QueryAllBalancesRequest = data.try_into()?;
1163 let bin_res = self.query(
1164 api,
1165 storage,
1166 block,
1167 BankQuery::AllBalances {
1168 address: msg.address,
1169 }
1170 .into(),
1171 )?;
1172
1173 let bank_res: AllBalanceResponse = from_binary(&bin_res)?;
1174
1175 let res = QueryAllBalancesResponse {
1176 balances: bank_res
1177 .amount
1178 .into_iter()
1179 .map(|c| c.into())
1180 .collect::<Vec<_>>(),
1181 pagination: None,
1182 };
1183 Ok(to_binary(&res)?)
1184 }
1185 QUERY_BALANCE_PATH => {
1186 let req: QueryBalanceRequest = data.try_into()?;
1187 let bin_res = self.query(
1188 api,
1189 storage,
1190 block,
1191 BankQuery::Balance {
1192 address: req.address,
1193 denom: req.denom,
1194 }
1195 .into(),
1196 )?;
1197
1198 let res: BalanceResponse = from_binary(&bin_res)?;
1199 let res = QueryBalanceResponse {
1200 balance: Some(res.amount.into()),
1201 };
1202
1203 Ok(to_binary(&res)?)
1204 }
1205 QUERY_SUPPLY_PATH => {
1206 let req: QuerySupplyOfRequest = data.try_into()?;
1207 let req = BankQuery::Supply { denom: req.denom };
1208
1209 let bin_res = self.query(api, storage, block, req.into())?;
1210
1211 let res: SupplyResponse = from_binary(&bin_res)?;
1212 let res = QuerySupplyOfResponse {
1213 amount: Some(res.amount.into()),
1214 };
1215
1216 Ok(to_binary(&res)?)
1217 }
1218 QUERY_WASM_CONTRACT_SMART_PATH => {
1220 let req: QuerySmartContractStateRequest = data.try_into()?;
1221 let query = WasmQuery::Smart {
1222 contract_addr: req.address,
1223 msg: req.query_data.into(),
1224 };
1225 let bin_res = self.query(api, storage, block, query.into())?;
1226
1227 let res = QuerySmartContractStateResponse {
1228 data: bin_res.into(),
1229 };
1230 Ok(to_binary(&res)?)
1231 }
1232 QUERY_WASM_CONTRACT_INFO_PATH => {
1233 let req: QueryContractInfoRequest = data.try_into()?;
1234 let query = WasmQuery::ContractInfo {
1235 contract_addr: req.address.clone(),
1236 };
1237 let bin_res = self.query(api, storage, block, query.into())?;
1238
1239 let res: ContractInfoResponse = from_binary(&bin_res)?;
1240 let res = QueryContractInfoResponse {
1241 address: req.address,
1242 contract_info: Some(ContractInfo {
1243 code_id: res.code_id,
1244 creator: res.creator,
1245 admin: res.admin.unwrap_or_default(),
1246 label: "".to_string(),
1247 created: None,
1248 ibc_port_id: res.ibc_port.unwrap_or_default(),
1249 extension: None,
1250 }),
1251 };
1252 Ok(to_binary(&res)?)
1253 }
1254 _ => self.stargate.query(
1256 api,
1257 storage,
1258 &querier,
1259 block,
1260 StargateMsg {
1261 type_url: path,
1262 value: data,
1263 },
1264 ),
1265 }
1266 }
1267 _ => unimplemented!(),
1268 }
1269 }
1270
1271 fn sudo(
1272 &self,
1273 api: &dyn Api,
1274 storage: &mut dyn Storage,
1275 block: &BlockInfo,
1276 msg: SudoMsg,
1277 ) -> AnyResult<AppResponse> {
1278 match msg {
1279 SudoMsg::Wasm(msg) => {
1280 self.wasm
1281 .sudo(api, msg.contract_addr, storage, self, block, msg.msg)
1282 }
1283 SudoMsg::Bank(msg) => self.bank.sudo(api, storage, self, block, msg),
1284 SudoMsg::Staking(msg) => self.staking.sudo(api, storage, self, block, msg),
1285 SudoMsg::Custom(_) => unimplemented!(),
1286 }
1287 }
1288}
1289
1290pub struct MockRouter<ExecC, QueryC>(PhantomData<(ExecC, QueryC)>);
1291
1292impl Default for MockRouter<Empty, Empty> {
1293 fn default() -> Self {
1294 Self::new()
1295 }
1296}
1297
1298impl<ExecC, QueryC> MockRouter<ExecC, QueryC> {
1299 pub fn new() -> Self
1300 where
1301 QueryC: CustomQuery,
1302 {
1303 MockRouter(PhantomData)
1304 }
1305}
1306
1307impl<ExecC, QueryC> CosmosRouter for MockRouter<ExecC, QueryC>
1308where
1309 QueryC: CustomQuery,
1310{
1311 type ExecC = ExecC;
1312 type QueryC = QueryC;
1313
1314 fn execute(
1315 &self,
1316 _api: &dyn Api,
1317 _storage: &mut dyn Storage,
1318 _block: &BlockInfo,
1319 _sender: Addr,
1320 _msg: CosmosMsg<Self::ExecC>,
1321 ) -> AnyResult<AppResponse> {
1322 panic!("Cannot execute MockRouters");
1323 }
1324
1325 fn query(
1326 &self,
1327 _api: &dyn Api,
1328 _storage: &dyn Storage,
1329 _block: &BlockInfo,
1330 _request: QueryRequest<Self::QueryC>,
1331 ) -> AnyResult<Binary> {
1332 panic!("Cannot query MockRouters");
1333 }
1334
1335 fn sudo(
1336 &self,
1337 _api: &dyn Api,
1338 _storage: &mut dyn Storage,
1339 _block: &BlockInfo,
1340 _msg: SudoMsg,
1341 ) -> AnyResult<AppResponse> {
1342 panic!("Cannot sudo MockRouters");
1343 }
1344}
1345
1346pub struct RouterQuerier<'a, ExecC, QueryC> {
1347 router: &'a dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
1348 api: &'a dyn Api,
1349 storage: &'a dyn Storage,
1350 block_info: &'a BlockInfo,
1351}
1352
1353impl<'a, ExecC, QueryC> RouterQuerier<'a, ExecC, QueryC> {
1354 pub fn new(
1355 router: &'a dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
1356 api: &'a dyn Api,
1357 storage: &'a dyn Storage,
1358 block_info: &'a BlockInfo,
1359 ) -> Self {
1360 Self {
1361 router,
1362 api,
1363 storage,
1364 block_info,
1365 }
1366 }
1367}
1368
1369impl<'a, ExecC, QueryC> Querier for RouterQuerier<'a, ExecC, QueryC>
1370where
1371 ExecC: Clone + fmt::Debug + PartialEq + JsonSchema + DeserializeOwned + 'static,
1372 QueryC: CustomQuery + DeserializeOwned + 'static,
1373{
1374 fn raw_query(&self, bin_request: &[u8]) -> QuerierResult {
1375 let request: QueryRequest<QueryC> = match from_slice(bin_request) {
1376 Ok(v) => v,
1377 Err(e) => {
1378 return SystemResult::Err(SystemError::InvalidRequest {
1379 error: format!("Parsing query request: {}", e),
1380 request: bin_request.into(),
1381 })
1382 }
1383 };
1384 let contract_result: ContractResult<Binary> = self
1385 .router
1386 .query(self.api, self.storage, self.block_info, request)
1387 .into();
1388 SystemResult::Ok(contract_result)
1389 }
1390}
1391
1392#[cfg(test)]
1393mod test {
1394 use super::*;
1395 use cosmwasm_std::testing::MockQuerier;
1396 use cosmwasm_std::{
1397 coin, coins, to_binary, AllBalanceResponse, Attribute, BankMsg, BankQuery, Coin, Event,
1398 OverflowError, OverflowOperation, Reply, StdError, StdResult, SubMsg, WasmMsg,
1399 };
1400
1401 use crate::error::Error;
1402 use crate::test_helpers::contracts::{caller, echo, error, hackatom, payout, reflect};
1403 use crate::test_helpers::{CustomMsg, EmptyMsg};
1404 use crate::transactions::StorageTransaction;
1405
1406 fn get_balance<BankT, ApiT, StorageT, CustomT, WasmT, StargateT>(
1407 app: &App<
1408 BankT,
1409 ApiT,
1410 StorageT,
1411 CustomT,
1412 WasmT,
1413 StakeKeeper,
1414 DistributionKeeper,
1415 FailingModule<IbcMsg, IbcQuery, Empty>,
1416 FailingModule<GovMsg, Empty, Empty>,
1417 StargateT,
1418 >,
1419 addr: &Addr,
1420 ) -> Vec<Coin>
1421 where
1422 CustomT::ExecT: Clone + fmt::Debug + PartialEq + JsonSchema + DeserializeOwned + 'static,
1423 CustomT::QueryT: CustomQuery + DeserializeOwned + 'static,
1424 WasmT: Wasm<CustomT::ExecT, CustomT::QueryT>,
1425 BankT: Bank,
1426 ApiT: Api,
1427 StorageT: Storage,
1428 CustomT: Module,
1429 StargateT: Stargate<CustomT::ExecT, CustomT::QueryT>,
1430 {
1431 app.wrap().query_all_balances(addr).unwrap()
1432 }
1433
1434 #[test]
1435 fn update_block() {
1436 let app = App::default();
1437
1438 let old_block = app.block_info();
1439 let BlockInfo { time, height, .. } = old_block;
1440
1441 app.update_block(next_block);
1442
1443 assert_eq!(time.plus_seconds(5), app.block.borrow().time);
1444 assert_eq!(height + 1, app.block.borrow().height);
1445 }
1446
1447 #[test]
1448 fn send_tokens() {
1449 let owner = Addr::unchecked("owner");
1450 let rcpt = Addr::unchecked("receiver");
1451 let init_funds = vec![coin(20, "btc"), coin(100, "eth")];
1452 let rcpt_funds = vec![coin(5, "btc")];
1453
1454 let app = App::new(|router, _, storage| {
1455 router
1457 .bank
1458 .init_balance(storage, &owner, init_funds)
1459 .unwrap();
1460 router
1461 .bank
1462 .init_balance(storage, &rcpt, rcpt_funds)
1463 .unwrap();
1464 });
1465
1466 let to_send = vec![coin(30, "eth"), coin(5, "btc")];
1468 let msg: cosmwasm_std::CosmosMsg = BankMsg::Send {
1469 to_address: rcpt.clone().into(),
1470 amount: to_send,
1471 }
1472 .into();
1473 app.execute(owner.clone(), msg.clone()).unwrap();
1474 let rich = get_balance(&app, &owner);
1475 assert_eq!(vec![coin(15, "btc"), coin(70, "eth")], rich);
1476 let poor = get_balance(&app, &rcpt);
1477 assert_eq!(vec![coin(10, "btc"), coin(30, "eth")], poor);
1478
1479 app.execute(rcpt.clone(), msg).unwrap();
1481
1482 let msg = BankMsg::Send {
1484 to_address: rcpt.into(),
1485 amount: coins(20, "btc"),
1486 }
1487 .into();
1488 app.execute(owner.clone(), msg).unwrap_err();
1489
1490 let rich = get_balance(&app, &owner);
1491 assert_eq!(vec![coin(15, "btc"), coin(70, "eth")], rich);
1492 }
1493
1494 #[test]
1495 fn simple_contract() {
1496 let owner = Addr::unchecked("owner");
1498 let init_funds = vec![coin(20, "btc"), coin(100, "eth")];
1499
1500 let app = App::new(|router, _, storage| {
1501 router
1502 .bank
1503 .init_balance(storage, &owner, init_funds)
1504 .unwrap();
1505 });
1506
1507 let code_id = app.store_code(payout::contract());
1509 let msg = payout::InstantiateMessage {
1510 payout: coin(5, "eth"),
1511 };
1512 let contract_addr = app
1513 .instantiate_contract(
1514 code_id,
1515 owner.clone(),
1516 &msg,
1517 &coins(23, "eth"),
1518 "Payout",
1519 None,
1520 )
1521 .unwrap();
1522
1523 let contract_data = app.contract_data(&contract_addr).unwrap();
1524 assert_eq!(
1525 contract_data,
1526 ContractData {
1527 code_id: code_id as usize,
1528 creator: owner.clone(),
1529 admin: None,
1530 label: "Payout".to_owned(),
1531 created: app.block_info().height
1532 }
1533 );
1534
1535 let sender = get_balance(&app, &owner);
1537 assert_eq!(sender, vec![coin(20, "btc"), coin(77, "eth")]);
1538 let funds = get_balance(&app, &contract_addr);
1540 assert_eq!(funds, coins(23, "eth"));
1541
1542 let random = Addr::unchecked("random");
1544 let funds = get_balance(&app, &random);
1545 assert_eq!(funds, vec![]);
1546
1547 let res = app
1549 .execute_contract(random.clone(), contract_addr.clone(), &EmptyMsg {}, &[])
1550 .unwrap();
1551 assert_eq!(3, res.events.len());
1552
1553 let payout_exec = &res.events[0];
1555 assert_eq!(payout_exec.ty.as_str(), "execute");
1556 assert_eq!(payout_exec.attributes, [("_contract_addr", &contract_addr)]);
1557
1558 let custom_attrs = res.custom_attrs(1);
1560 assert_eq!(custom_attrs, [("action", "payout")]);
1561
1562 let expected_transfer = Event::new("transfer")
1564 .add_attribute("recipient", "random")
1565 .add_attribute("sender", &contract_addr)
1566 .add_attribute("amount", "5eth");
1567 assert_eq!(&expected_transfer, &res.events[2]);
1568
1569 let funds = get_balance(&app, &random);
1571 assert_eq!(funds, coins(5, "eth"));
1572 let funds = get_balance(&app, &contract_addr);
1574 assert_eq!(funds, coins(18, "eth"));
1575 }
1576
1577 #[test]
1578 fn reflect_success() {
1579 let owner = Addr::unchecked("owner");
1581 let init_funds = vec![coin(20, "btc"), coin(100, "eth")];
1582
1583 let app = custom_app::<CustomMsg, Empty, _>(|router, _, storage| {
1584 router
1585 .bank
1586 .init_balance(storage, &owner, init_funds)
1587 .unwrap();
1588 });
1589
1590 let payout_id = app.store_code(payout::contract());
1592 let msg = payout::InstantiateMessage {
1593 payout: coin(5, "eth"),
1594 };
1595 let payout_addr = app
1596 .instantiate_contract(
1597 payout_id,
1598 owner.clone(),
1599 &msg,
1600 &coins(23, "eth"),
1601 "Payout",
1602 None,
1603 )
1604 .unwrap();
1605
1606 let reflect_id = app.store_code(reflect::contract());
1608 let reflect_addr = app
1609 .instantiate_contract(reflect_id, owner, &EmptyMsg {}, &[], "Reflect", None)
1610 .unwrap();
1611
1612 let funds = get_balance(&app, &reflect_addr);
1614 assert_eq!(funds, vec![]);
1615 let qres: payout::CountResponse = app
1617 .wrap()
1618 .query_wasm_smart(&reflect_addr, &reflect::QueryMsg::Count {})
1619 .unwrap();
1620 assert_eq!(0, qres.count);
1621
1622 let msg = SubMsg::new(WasmMsg::Execute {
1624 contract_addr: payout_addr.clone().into(),
1625 msg: b"{}".into(),
1626 funds: vec![],
1627 });
1628 let msgs = reflect::Message {
1629 messages: vec![msg],
1630 };
1631 let res = app
1632 .execute_contract(Addr::unchecked("random"), reflect_addr.clone(), &msgs, &[])
1633 .unwrap();
1634
1635 assert_eq!(4, res.events.len(), "{:?}", res.events);
1637
1638 let ref_exec = &res.events[0];
1640 assert_eq!(ref_exec.ty.as_str(), "execute");
1641 assert_eq!(ref_exec.attributes, [("_contract_addr", &reflect_addr)]);
1642
1643 let payout_exec = &res.events[1];
1645 assert_eq!(payout_exec.ty.as_str(), "execute");
1646 assert_eq!(payout_exec.attributes, [("_contract_addr", &payout_addr)]);
1647
1648 let payout = &res.events[2];
1649 assert_eq!(payout.ty.as_str(), "wasm");
1650 assert_eq!(
1651 payout.attributes,
1652 [
1653 ("_contract_addr", payout_addr.as_str()),
1654 ("action", "payout")
1655 ]
1656 );
1657
1658 let second = &res.events[3];
1660 assert_eq!(second.ty.as_str(), "transfer");
1661 assert_eq!(3, second.attributes.len());
1662 assert_eq!(second.attributes[0], ("recipient", &reflect_addr));
1663 assert_eq!(second.attributes[1], ("sender", &payout_addr));
1664 assert_eq!(second.attributes[2], ("amount", "5eth"));
1665
1666 let funds = get_balance(&app, &reflect_addr);
1668 assert_eq!(funds, coins(5, "eth"));
1669
1670 let qres: payout::CountResponse = app
1672 .wrap()
1673 .query_wasm_smart(&reflect_addr, &reflect::QueryMsg::Count {})
1674 .unwrap();
1675 assert_eq!(1, qres.count);
1676 }
1677
1678 #[test]
1679 fn reflect_error() {
1680 let owner = Addr::unchecked("owner");
1682 let init_funds = vec![coin(20, "btc"), coin(100, "eth")];
1683
1684 let app = custom_app::<CustomMsg, Empty, _>(|router, _, storage| {
1685 router
1686 .bank
1687 .init_balance(storage, &owner, init_funds)
1688 .unwrap();
1689 });
1690
1691 let reflect_id = app.store_code(reflect::contract());
1693 let reflect_addr = app
1694 .instantiate_contract(
1695 reflect_id,
1696 owner,
1697 &EmptyMsg {},
1698 &coins(40, "eth"),
1699 "Reflect",
1700 None,
1701 )
1702 .unwrap();
1703
1704 let funds = get_balance(&app, &reflect_addr);
1706 assert_eq!(funds, coins(40, "eth"));
1707 let random = Addr::unchecked("random");
1708
1709 let msg = SubMsg::new(BankMsg::Send {
1711 to_address: random.clone().into(),
1712 amount: coins(7, "eth"),
1713 });
1714 let msgs = reflect::Message {
1715 messages: vec![msg],
1716 };
1717 let res = app
1718 .execute_contract(random.clone(), reflect_addr.clone(), &msgs, &[])
1719 .unwrap();
1720 assert_eq!(2, res.events.len());
1722 let exec = &res.events[0];
1724 assert_eq!(exec.ty.as_str(), "execute");
1725 assert_eq!(exec.attributes, [("_contract_addr", &reflect_addr)]);
1726 let transfer = &res.events[1];
1728 assert_eq!(transfer.ty.as_str(), "transfer");
1729
1730 let funds = get_balance(&app, &random);
1732 assert_eq!(funds, coins(7, "eth"));
1733
1734 let qres: payout::CountResponse = app
1736 .wrap()
1737 .query_wasm_smart(&reflect_addr, &reflect::QueryMsg::Count {})
1738 .unwrap();
1739 assert_eq!(1, qres.count);
1740
1741 let msg = SubMsg::new(BankMsg::Send {
1743 to_address: random.clone().into(),
1744 amount: coins(8, "eth"),
1745 });
1746 let msg2 = SubMsg::new(BankMsg::Send {
1747 to_address: random.clone().into(),
1748 amount: coins(3, "btc"),
1749 });
1750 let msgs = reflect::Message {
1751 messages: vec![msg, msg2],
1752 };
1753 let err = app
1754 .execute_contract(random.clone(), reflect_addr.clone(), &msgs, &[])
1755 .unwrap_err();
1756 assert_eq!(
1757 StdError::overflow(OverflowError::new(OverflowOperation::Sub, 0, 3)),
1758 err.downcast().unwrap()
1759 );
1760
1761 let funds = get_balance(&app, &random);
1763 assert_eq!(funds, coins(7, "eth"));
1764
1765 let qres: payout::CountResponse = app
1767 .wrap()
1768 .query_wasm_smart(&reflect_addr, &reflect::QueryMsg::Count {})
1769 .unwrap();
1770 assert_eq!(1, qres.count);
1771 }
1772
1773 #[test]
1774 fn sudo_works() {
1775 let owner = Addr::unchecked("owner");
1776 let init_funds = vec![coin(100, "eth")];
1777
1778 let app = App::new(|router, _, storage| {
1779 router
1780 .bank
1781 .init_balance(storage, &owner, init_funds)
1782 .unwrap();
1783 });
1784
1785 let payout_id = app.store_code(payout::contract());
1786 let msg = payout::InstantiateMessage {
1787 payout: coin(5, "eth"),
1788 };
1789 let payout_addr = app
1790 .instantiate_contract(payout_id, owner, &msg, &coins(23, "eth"), "Payout", None)
1791 .unwrap();
1792
1793 let payout::CountResponse { count } = app
1795 .wrap()
1796 .query_wasm_smart(&payout_addr, &payout::QueryMsg::Count {})
1797 .unwrap();
1798 assert_eq!(1, count);
1799
1800 let msg = payout::SudoMsg { set_count: 25 };
1802 app.wasm_sudo(payout_addr.clone(), &msg).unwrap();
1803
1804 let payout::CountResponse { count } = app
1806 .wrap()
1807 .query_wasm_smart(&payout_addr, &payout::QueryMsg::Count {})
1808 .unwrap();
1809 assert_eq!(25, count);
1810
1811 let msg = payout::SudoMsg { set_count: 49 };
1813 let sudo_msg = WasmSudo {
1814 contract_addr: payout_addr.clone(),
1815 msg: to_binary(&msg).unwrap(),
1816 };
1817 app.sudo(sudo_msg.into()).unwrap();
1818
1819 let payout::CountResponse { count } = app
1820 .wrap()
1821 .query_wasm_smart(&payout_addr, &payout::QueryMsg::Count {})
1822 .unwrap();
1823 assert_eq!(49, count);
1824 }
1825
1826 mod custom_handler {
1829 use super::*;
1830
1831 use anyhow::{bail, Result as AnyResult};
1832 use cw_storage_plus::Item;
1833 use serde::{Deserialize, Serialize};
1834
1835 use crate::Executor;
1836
1837 const LOTTERY: Item<Coin> = Item::new("lottery");
1838 const PITY: Item<Coin> = Item::new("pity");
1839
1840 #[derive(Clone, std::fmt::Debug, PartialEq, JsonSchema, Serialize, Deserialize)]
1841 struct CustomMsg {
1842 lucky_winner: String,
1844 runner_up: String,
1846 }
1847
1848 struct CustomHandler {}
1849
1850 impl Module for CustomHandler {
1851 type ExecT = CustomMsg;
1852 type QueryT = Empty;
1853 type SudoT = Empty;
1854
1855 fn execute<ExecC, QueryC>(
1856 &self,
1857 api: &dyn Api,
1858 storage: &mut dyn Storage,
1859 router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
1860 block: &BlockInfo,
1861 _sender: Addr,
1862 msg: Self::ExecT,
1863 ) -> AnyResult<AppResponse>
1864 where
1865 ExecC:
1866 std::fmt::Debug + Clone + PartialEq + JsonSchema + DeserializeOwned + 'static,
1867 QueryC: CustomQuery + DeserializeOwned + 'static,
1868 {
1869 let lottery = LOTTERY.load(storage)?;
1870 let pity = PITY.load(storage)?;
1871
1872 let mint = BankSudo::Mint {
1874 to_address: msg.lucky_winner.clone(),
1875 amount: vec![lottery],
1876 };
1877 router.sudo(api, storage, block, mint.into())?;
1878
1879 let transfer = BankMsg::Send {
1881 to_address: msg.runner_up,
1882 amount: vec![pity],
1883 };
1884 let rcpt = api.addr_validate(&msg.lucky_winner)?;
1885 router.execute(api, storage, block, rcpt, transfer.into())?;
1886
1887 Ok(AppResponse::default())
1888 }
1889
1890 fn sudo<ExecC, QueryC>(
1891 &self,
1892 _api: &dyn Api,
1893 _storage: &mut dyn Storage,
1894 _router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
1895 _block: &BlockInfo,
1896 _msg: Self::SudoT,
1897 ) -> AnyResult<AppResponse>
1898 where
1899 ExecC:
1900 std::fmt::Debug + Clone + PartialEq + JsonSchema + DeserializeOwned + 'static,
1901 QueryC: CustomQuery + DeserializeOwned + 'static,
1902 {
1903 bail!("sudo not implemented for CustomHandler")
1904 }
1905
1906 fn query(
1907 &self,
1908 _api: &dyn Api,
1909 _storage: &dyn Storage,
1910 _querier: &dyn Querier,
1911 _block: &BlockInfo,
1912 _request: Self::QueryT,
1913 ) -> AnyResult<Binary> {
1914 bail!("query not implemented for CustomHandler")
1915 }
1916 }
1917
1918 impl CustomHandler {
1919 pub fn set_payout(
1921 &self,
1922 storage: &mut dyn Storage,
1923 lottery: Coin,
1924 pity: Coin,
1925 ) -> AnyResult<()> {
1926 LOTTERY.save(storage, &lottery)?;
1927 PITY.save(storage, &pity)?;
1928 Ok(())
1929 }
1930 }
1931
1932 #[test]
1934 fn dispatches_messages() {
1935 let winner = "winner".to_string();
1936 let second = "second".to_string();
1937
1938 let denom = "tix";
1940 let lottery = coin(54321, denom);
1941 let bonus = coin(12321, denom);
1942
1943 let app = BasicAppBuilder::<CustomMsg, Empty>::new_custom()
1944 .with_custom(CustomHandler {})
1945 .build(|router, _, storage| {
1946 router
1947 .custom
1948 .set_payout(storage, lottery.clone(), bonus.clone())
1949 .unwrap();
1950 });
1951
1952 let start = app.wrap().query_balance(&winner, denom).unwrap();
1954 assert_eq!(start, coin(0, denom));
1955
1956 let msg = CosmosMsg::Custom(CustomMsg {
1958 lucky_winner: winner.clone(),
1959 runner_up: second.clone(),
1960 });
1961 app.execute(Addr::unchecked("anyone"), msg).unwrap();
1962
1963 let big_win = app.wrap().query_balance(&winner, denom).unwrap();
1965 assert_eq!(big_win, coin(42000, denom));
1966 let little_win = app.wrap().query_balance(&second, denom).unwrap();
1967 assert_eq!(little_win, bonus);
1968 }
1969 }
1970
1971 #[test]
1972 fn reflect_submessage_reply_works() {
1973 let owner = Addr::unchecked("owner");
1975 let random = Addr::unchecked("random");
1976 let init_funds = vec![coin(20, "btc"), coin(100, "eth")];
1977
1978 let app = custom_app::<CustomMsg, Empty, _>(|router, _, storage| {
1979 router
1980 .bank
1981 .init_balance(storage, &owner, init_funds)
1982 .unwrap();
1983 });
1984
1985 let reflect_id = app.store_code(reflect::contract());
1987 let reflect_addr = app
1988 .instantiate_contract(
1989 reflect_id,
1990 owner,
1991 &EmptyMsg {},
1992 &coins(40, "eth"),
1993 "Reflect",
1994 None,
1995 )
1996 .unwrap();
1997
1998 let query = reflect::QueryMsg::Reply { id: 123 };
2000 let res: StdResult<Reply> = app.wrap().query_wasm_smart(&reflect_addr, &query);
2001 res.unwrap_err();
2002
2003 let msg = SubMsg::reply_always(
2005 BankMsg::Send {
2006 to_address: random.clone().into(),
2007 amount: coins(7, "eth"),
2008 },
2009 123,
2010 );
2011 let msgs = reflect::Message {
2012 messages: vec![msg],
2013 };
2014 let res = app
2015 .execute_contract(random.clone(), reflect_addr.clone(), &msgs, &[])
2016 .unwrap();
2017
2018 assert_eq!(4, res.events.len(), "{:?}", res.events);
2020 res.assert_event(&Event::new("execute").add_attribute("_contract_addr", &reflect_addr));
2021 res.assert_event(&Event::new("transfer").add_attribute("amount", "7eth"));
2022 res.assert_event(
2023 &Event::new("reply")
2024 .add_attribute("_contract_addr", reflect_addr.as_str())
2025 .add_attribute("mode", "handle_success"),
2026 );
2027 res.assert_event(&Event::new("wasm-custom").add_attribute("from", "reply"));
2028
2029 let res: Reply = app.wrap().query_wasm_smart(&reflect_addr, &query).unwrap();
2031 assert_eq!(res.id, 123);
2032 let reply = res.result.unwrap();
2034 assert_eq!(1, reply.events.len());
2035 AppResponse::from(reply)
2036 .assert_event(&Event::new("transfer").add_attribute("amount", "7eth"));
2037
2038 let msg = SubMsg::reply_always(
2040 BankMsg::Send {
2041 to_address: random.clone().into(),
2042 amount: coins(300, "btc"),
2043 },
2044 456,
2045 );
2046 let msgs = reflect::Message {
2047 messages: vec![msg],
2048 };
2049 let _res = app
2050 .execute_contract(random, reflect_addr.clone(), &msgs, &[])
2051 .unwrap();
2052
2053 let query = reflect::QueryMsg::Reply { id: 456 };
2055 let res: Reply = app.wrap().query_wasm_smart(&reflect_addr, &query).unwrap();
2056 assert_eq!(res.id, 456);
2057 assert!(res.result.is_err());
2058 }
2060
2061 fn query_router<BankT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>(
2062 router: &Router<BankT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>,
2063 api: &dyn Api,
2064 storage: &dyn Storage,
2065 rcpt: &Addr,
2066 ) -> Vec<Coin>
2067 where
2068 CustomT::ExecT: Clone + fmt::Debug + PartialEq + JsonSchema,
2069 CustomT::QueryT: CustomQuery + DeserializeOwned,
2070 WasmT: Wasm<CustomT::ExecT, CustomT::QueryT>,
2071 BankT: Bank,
2072 CustomT: Module,
2073 StakingT: Staking,
2074 DistrT: Distribution,
2075 {
2076 let query = BankQuery::AllBalances {
2077 address: rcpt.into(),
2078 };
2079 let block = mock_env().block;
2080 let querier: MockQuerier<CustomT::QueryT> = MockQuerier::new(&[]);
2081 let res = router
2082 .bank
2083 .query(api, storage, &querier, &block, query)
2084 .unwrap();
2085 let val: AllBalanceResponse = from_slice(&res).unwrap();
2086 val.amount
2087 }
2088
2089 fn query_app<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>(
2090 app: &App<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>,
2091 rcpt: &Addr,
2092 ) -> Vec<Coin>
2093 where
2094 CustomT::ExecT:
2095 std::fmt::Debug + PartialEq + Clone + JsonSchema + DeserializeOwned + 'static,
2096 CustomT::QueryT: CustomQuery + DeserializeOwned + 'static,
2097 WasmT: Wasm<CustomT::ExecT, CustomT::QueryT>,
2098 BankT: Bank,
2099 ApiT: Api,
2100 StorageT: Storage,
2101 CustomT: Module,
2102 StakingT: Staking,
2103 DistrT: Distribution,
2104 IbcT: Ibc,
2105 GovT: Gov,
2106 StargateT: Stargate<CustomT::ExecT, CustomT::QueryT>,
2107 {
2108 let query = BankQuery::AllBalances {
2109 address: rcpt.into(),
2110 }
2111 .into();
2112 let val: AllBalanceResponse = app.wrap().query(&query).unwrap();
2113 val.amount
2114 }
2115
2116 #[test]
2117 fn multi_level_bank_cache() {
2118 let owner = Addr::unchecked("owner");
2120 let rcpt = Addr::unchecked("recipient");
2121 let init_funds = vec![coin(20, "btc"), coin(100, "eth")];
2122
2123 let app = App::new(|router, _, storage| {
2124 router
2125 .bank
2126 .init_balance(storage, &owner, init_funds)
2127 .unwrap();
2128 });
2129
2130 let storage = app.storage.take();
2132 let mut cache = StorageTransaction::new(&storage);
2133 let msg = BankMsg::Send {
2134 to_address: rcpt.clone().into(),
2135 amount: coins(25, "eth"),
2136 };
2137 app.router
2138 .borrow()
2139 .execute(
2140 &app.api,
2141 &mut cache,
2142 &app.block.borrow(),
2143 owner.clone(),
2144 msg.into(),
2145 )
2146 .unwrap();
2147
2148 let cached_rcpt = query_router(&app.router.borrow(), &app.api, &cache, &rcpt);
2150 assert_eq!(coins(25, "eth"), cached_rcpt);
2151 let router_rcpt = query_app(&app, &rcpt);
2152 assert_eq!(router_rcpt, vec![]);
2153
2154 transactional(&mut cache, |cache2, read| {
2156 let msg = BankMsg::Send {
2157 to_address: rcpt.clone().into(),
2158 amount: coins(12, "eth"),
2159 };
2160 app.router
2161 .borrow()
2162 .execute(&app.api, cache2, &app.block.borrow(), owner, msg.into())
2163 .unwrap();
2164
2165 let cached_rcpt = query_router(&app.router.borrow(), &app.api, read, &rcpt);
2167 assert_eq!(coins(25, "eth"), cached_rcpt);
2168 let cached2_rcpt = query_router(&app.router.borrow(), &app.api, cache2, &rcpt);
2169 assert_eq!(coins(37, "eth"), cached2_rcpt);
2170 Ok(())
2171 })
2172 .unwrap();
2173
2174 cache.prepare().commit(app.storage.borrow_mut().deref_mut());
2176
2177 let committed = query_app(&app, &rcpt);
2178 assert_eq!(coins(37, "eth"), committed);
2179 }
2180
2181 #[test]
2182 fn sent_funds_properly_visible_on_execution() {
2183 let owner = Addr::unchecked("owner");
2189 let beneficiary = Addr::unchecked("beneficiary");
2190 let init_funds = coins(30, "btc");
2191
2192 let app = App::new(|router, _, storage| {
2193 router
2194 .bank
2195 .init_balance(storage, &owner, init_funds)
2196 .unwrap();
2197 });
2198
2199 let contract_id = app.store_code(hackatom::contract());
2200 let contract = app
2201 .instantiate_contract(
2202 contract_id,
2203 owner.clone(),
2204 &hackatom::InstantiateMsg {
2205 beneficiary: beneficiary.as_str().to_owned(),
2206 },
2207 &coins(10, "btc"),
2208 "Hackatom",
2209 None,
2210 )
2211 .unwrap();
2212
2213 app.execute_contract(
2214 owner.clone(),
2215 contract.clone(),
2216 &EmptyMsg {},
2217 &coins(20, "btc"),
2218 )
2219 .unwrap();
2220
2221 assert_eq!(get_balance(&app, &owner), &[]);
2224 assert_eq!(get_balance(&app, &contract), &[]);
2225 assert_eq!(get_balance(&app, &beneficiary), coins(30, "btc"));
2226 }
2227
2228 #[test]
2229 fn sent_wasm_migration_works() {
2230 let owner = Addr::unchecked("owner");
2238 let beneficiary = Addr::unchecked("beneficiary");
2239 let init_funds = coins(30, "btc");
2240
2241 let mut app = App::new(|router, _, storage| {
2242 router
2243 .bank
2244 .init_balance(storage, &owner, init_funds)
2245 .unwrap();
2246 });
2247
2248 let contract_id = app.store_code(hackatom::contract());
2250 let contract = app
2251 .instantiate_contract(
2252 contract_id,
2253 owner.clone(),
2254 &hackatom::InstantiateMsg {
2255 beneficiary: beneficiary.as_str().to_owned(),
2256 },
2257 &coins(20, "btc"),
2258 "Hackatom",
2259 Some(owner.to_string()),
2260 )
2261 .unwrap();
2262
2263 let info = app.contract_data(&contract).unwrap();
2265 assert_eq!(info.admin, Some(owner.clone()));
2266 let state: hackatom::InstantiateMsg = app
2268 .wrap()
2269 .query_wasm_smart(&contract, &hackatom::QueryMsg::Beneficiary {})
2270 .unwrap();
2271 assert_eq!(state.beneficiary, beneficiary);
2272
2273 let random = Addr::unchecked("random");
2275 let migrate_msg = hackatom::MigrateMsg {
2276 new_guy: random.to_string(),
2277 };
2278 app.migrate_contract(beneficiary, contract.clone(), &migrate_msg, contract_id)
2279 .unwrap_err();
2280
2281 app.migrate_contract(
2283 owner.clone(),
2284 contract.clone(),
2285 &migrate_msg,
2286 contract_id + 7,
2287 )
2288 .unwrap_err();
2289
2290 app.migrate_contract(owner, contract.clone(), &migrate_msg, contract_id)
2292 .unwrap();
2293
2294 let state: hackatom::InstantiateMsg = app
2296 .wrap()
2297 .query_wasm_smart(&contract, &hackatom::QueryMsg::Beneficiary {})
2298 .unwrap();
2299 assert_eq!(state.beneficiary, random);
2300 }
2301
2302 #[test]
2303 fn send_update_admin_works() {
2304 let owner = Addr::unchecked("owner");
2311 let owner2 = Addr::unchecked("owner2");
2312 let beneficiary = Addr::unchecked("beneficiary");
2313
2314 let app = App::default();
2315
2316 let contract_id = app.store_code(hackatom::contract());
2318 let contract = app
2319 .instantiate_contract(
2320 contract_id,
2321 owner.clone(),
2322 &hackatom::InstantiateMsg {
2323 beneficiary: beneficiary.as_str().to_owned(),
2324 },
2325 &[],
2326 "Hackatom",
2327 Some(owner.to_string()),
2328 )
2329 .unwrap();
2330
2331 let info = app.contract_data(&contract).unwrap();
2333 assert_eq!(info.admin, Some(owner.clone()));
2334
2335 app.execute(
2337 owner.clone(),
2338 CosmosMsg::Wasm(WasmMsg::UpdateAdmin {
2339 contract_addr: contract.to_string(),
2340 admin: owner2.to_string(),
2341 }),
2342 )
2343 .unwrap();
2344
2345 let info = app.contract_data(&contract).unwrap();
2347 assert_eq!(info.admin, Some(owner2.clone()));
2348
2349 app.execute(
2351 owner.clone(),
2352 CosmosMsg::Wasm(WasmMsg::UpdateAdmin {
2353 contract_addr: contract.to_string(),
2354 admin: owner.to_string(),
2355 }),
2356 )
2357 .unwrap_err();
2358
2359 let info = app.contract_data(&contract).unwrap();
2361 assert_eq!(info.admin, Some(owner2));
2362 }
2363
2364 mod reply_data_overwrite {
2365 use super::*;
2366
2367 use echo::EXECUTE_REPLY_BASE_ID;
2368
2369 fn make_echo_submsg(
2370 contract: Addr,
2371 data: impl Into<Option<&'static str>>,
2372 sub_msg: Vec<SubMsg>,
2373 id: u64,
2374 ) -> SubMsg {
2375 let data = data.into().map(|s| s.to_owned());
2376 SubMsg::reply_always(
2377 CosmosMsg::Wasm(WasmMsg::Execute {
2378 contract_addr: contract.into(),
2379 msg: to_binary(&echo::Message {
2380 data,
2381 sub_msg,
2382 ..echo::Message::default()
2383 })
2384 .unwrap(),
2385 funds: vec![],
2386 }),
2387 id,
2388 )
2389 }
2390
2391 fn make_echo_submsg_no_reply(
2392 contract: Addr,
2393 data: impl Into<Option<&'static str>>,
2394 sub_msg: Vec<SubMsg>,
2395 ) -> SubMsg {
2396 let data = data.into().map(|s| s.to_owned());
2397 SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute {
2398 contract_addr: contract.into(),
2399 msg: to_binary(&echo::Message {
2400 data,
2401 sub_msg,
2402 ..echo::Message::default()
2403 })
2404 .unwrap(),
2405 funds: vec![],
2406 }))
2407 }
2408
2409 #[test]
2410 fn no_submsg() {
2411 let app = App::default();
2412
2413 let owner = Addr::unchecked("owner");
2414
2415 let contract_id = app.store_code(echo::contract());
2416 let contract = app
2417 .instantiate_contract(contract_id, owner.clone(), &EmptyMsg {}, &[], "Echo", None)
2418 .unwrap();
2419
2420 let response = app
2421 .execute_contract(
2422 owner,
2423 contract,
2424 &echo::Message::<Empty> {
2425 data: Some("Data".to_owned()),
2426 ..echo::Message::default()
2427 },
2428 &[],
2429 )
2430 .unwrap();
2431
2432 assert_eq!(response.data, Some(b"Data".into()));
2433 }
2434
2435 #[test]
2436 fn single_submsg() {
2437 let app = App::default();
2438
2439 let owner = Addr::unchecked("owner");
2440
2441 let contract_id = app.store_code(echo::contract());
2442 let contract = app
2443 .instantiate_contract(contract_id, owner.clone(), &EmptyMsg {}, &[], "Echo", None)
2444 .unwrap();
2445
2446 let response = app
2447 .execute_contract(
2448 owner,
2449 contract.clone(),
2450 &echo::Message {
2451 data: Some("First".to_owned()),
2452 sub_msg: vec![make_echo_submsg(
2453 contract,
2454 "Second",
2455 vec![],
2456 EXECUTE_REPLY_BASE_ID,
2457 )],
2458 ..echo::Message::default()
2459 },
2460 &[],
2461 )
2462 .unwrap();
2463
2464 assert_eq!(response.data, Some(b"Second".into()));
2465 }
2466
2467 #[test]
2468 fn single_submsg_no_reply() {
2469 let app = App::default();
2470
2471 let owner = Addr::unchecked("owner");
2472
2473 let contract_id = app.store_code(echo::contract());
2474 let contract = app
2475 .instantiate_contract(contract_id, owner.clone(), &EmptyMsg {}, &[], "Echo", None)
2476 .unwrap();
2477
2478 let response = app
2479 .execute_contract(
2480 owner,
2481 contract.clone(),
2482 &echo::Message {
2483 data: Some("First".to_owned()),
2484 sub_msg: vec![make_echo_submsg_no_reply(contract, "Second", vec![])],
2485 ..echo::Message::default()
2486 },
2487 &[],
2488 )
2489 .unwrap();
2490
2491 assert_eq!(response.data, Some(b"First".into()));
2492 }
2493
2494 #[test]
2495 fn single_no_submsg_data() {
2496 let app = App::default();
2497
2498 let owner = Addr::unchecked("owner");
2499
2500 let contract_id = app.store_code(echo::contract());
2501 let contract = app
2502 .instantiate_contract(contract_id, owner.clone(), &EmptyMsg {}, &[], "Echo", None)
2503 .unwrap();
2504
2505 let response = app
2506 .execute_contract(
2507 owner,
2508 contract.clone(),
2509 &echo::Message {
2510 data: Some("First".to_owned()),
2511 sub_msg: vec![make_echo_submsg(contract, None, vec![], 1)],
2512 ..echo::Message::default()
2513 },
2514 &[],
2515 )
2516 .unwrap();
2517
2518 assert_eq!(response.data, Some(b"First".into()));
2519 }
2520
2521 #[test]
2522 fn single_no_top_level_data() {
2523 let app = App::default();
2524
2525 let owner = Addr::unchecked("owner");
2526
2527 let contract_id = app.store_code(echo::contract());
2528 let contract = app
2529 .instantiate_contract(contract_id, owner.clone(), &EmptyMsg {}, &[], "Echo", None)
2530 .unwrap();
2531
2532 let response = app
2533 .execute_contract(
2534 owner,
2535 contract.clone(),
2536 &echo::Message {
2537 sub_msg: vec![make_echo_submsg(
2538 contract,
2539 "Second",
2540 vec![],
2541 EXECUTE_REPLY_BASE_ID,
2542 )],
2543 ..echo::Message::default()
2544 },
2545 &[],
2546 )
2547 .unwrap();
2548
2549 assert_eq!(response.data, Some(b"Second".into()));
2550 }
2551
2552 #[test]
2553 fn single_submsg_reply_returns_none() {
2554 let owner = Addr::unchecked("owner");
2556 let init_funds = coins(100, "tgd");
2557
2558 let app = custom_app::<CustomMsg, Empty, _>(|router, _, storage| {
2559 router
2560 .bank
2561 .init_balance(storage, &owner, init_funds)
2562 .unwrap();
2563 });
2564
2565 let reflect_id = app.store_code(reflect::contract());
2567 let reflect_addr = app
2568 .instantiate_contract(
2569 reflect_id,
2570 owner.clone(),
2571 &EmptyMsg {},
2572 &[],
2573 "Reflect",
2574 None,
2575 )
2576 .unwrap();
2577
2578 let echo_id = app.store_code(echo::custom_contract());
2580 let echo_addr = app
2581 .instantiate_contract(echo_id, owner.clone(), &EmptyMsg {}, &[], "Echo", None)
2582 .unwrap();
2583
2584 let echo_msg = echo::Message::<Empty> {
2588 data: Some("my echo".into()),
2589 events: vec![Event::new("echo").add_attribute("called", "true")],
2590 ..echo::Message::default()
2591 };
2592 let reflect_msg = reflect::Message {
2593 messages: vec![SubMsg::new(WasmMsg::Execute {
2594 contract_addr: echo_addr.to_string(),
2595 msg: to_binary(&echo_msg).unwrap(),
2596 funds: vec![],
2597 })],
2598 };
2599
2600 let res = app
2601 .execute_contract(owner, reflect_addr.clone(), &reflect_msg, &[])
2602 .unwrap();
2603
2604 assert_eq!(res.data, None);
2606 assert_eq!(res.events.len(), 3, "{:?}", res.events);
2608 res.assert_event(&Event::new("execute").add_attribute("_contract_addr", &reflect_addr));
2609 res.assert_event(&Event::new("execute").add_attribute("_contract_addr", &echo_addr));
2610 res.assert_event(&Event::new("wasm-echo"));
2611 }
2612
2613 #[test]
2614 fn multiple_submsg() {
2615 let app = App::default();
2616
2617 let owner = Addr::unchecked("owner");
2618
2619 let contract_id = app.store_code(echo::contract());
2620 let contract = app
2621 .instantiate_contract(contract_id, owner.clone(), &EmptyMsg {}, &[], "Echo", None)
2622 .unwrap();
2623
2624 let response = app
2625 .execute_contract(
2626 owner,
2627 contract.clone(),
2628 &echo::Message {
2629 data: Some("Orig".to_owned()),
2630 sub_msg: vec![
2631 make_echo_submsg(
2632 contract.clone(),
2633 None,
2634 vec![],
2635 EXECUTE_REPLY_BASE_ID + 1,
2636 ),
2637 make_echo_submsg(
2638 contract.clone(),
2639 "First",
2640 vec![],
2641 EXECUTE_REPLY_BASE_ID + 2,
2642 ),
2643 make_echo_submsg(
2644 contract.clone(),
2645 "Second",
2646 vec![],
2647 EXECUTE_REPLY_BASE_ID + 3,
2648 ),
2649 make_echo_submsg(contract, None, vec![], EXECUTE_REPLY_BASE_ID + 4),
2650 ],
2651 ..echo::Message::default()
2652 },
2653 &[],
2654 )
2655 .unwrap();
2656
2657 assert_eq!(response.data, Some(b"Second".into()));
2658 }
2659
2660 #[test]
2661 fn multiple_submsg_no_reply() {
2662 let app = App::default();
2663
2664 let owner = Addr::unchecked("owner");
2665
2666 let contract_id = app.store_code(echo::contract());
2667 let contract = app
2668 .instantiate_contract(contract_id, owner.clone(), &EmptyMsg {}, &[], "Echo", None)
2669 .unwrap();
2670
2671 let response = app
2672 .execute_contract(
2673 owner,
2674 contract.clone(),
2675 &echo::Message {
2676 data: Some("Orig".to_owned()),
2677 sub_msg: vec![
2678 make_echo_submsg_no_reply(contract.clone(), None, vec![]),
2679 make_echo_submsg_no_reply(contract.clone(), "First", vec![]),
2680 make_echo_submsg_no_reply(contract.clone(), "Second", vec![]),
2681 make_echo_submsg_no_reply(contract, None, vec![]),
2682 ],
2683 ..echo::Message::default()
2684 },
2685 &[],
2686 )
2687 .unwrap();
2688
2689 assert_eq!(response.data, Some(b"Orig".into()));
2690 }
2691
2692 #[test]
2693 fn multiple_submsg_mixed() {
2694 let app = App::default();
2695
2696 let owner = Addr::unchecked("owner");
2697
2698 let contract_id = app.store_code(echo::contract());
2699 let contract = app
2700 .instantiate_contract(contract_id, owner.clone(), &EmptyMsg {}, &[], "Echo", None)
2701 .unwrap();
2702
2703 let response = app
2704 .execute_contract(
2705 owner,
2706 contract.clone(),
2707 &echo::Message {
2708 sub_msg: vec![
2709 make_echo_submsg(
2710 contract.clone(),
2711 None,
2712 vec![],
2713 EXECUTE_REPLY_BASE_ID + 1,
2714 ),
2715 make_echo_submsg_no_reply(contract.clone(), "Hidden", vec![]),
2716 make_echo_submsg(
2717 contract.clone(),
2718 "Shown",
2719 vec![],
2720 EXECUTE_REPLY_BASE_ID + 2,
2721 ),
2722 make_echo_submsg(
2723 contract.clone(),
2724 None,
2725 vec![],
2726 EXECUTE_REPLY_BASE_ID + 3,
2727 ),
2728 make_echo_submsg_no_reply(contract, "Lost", vec![]),
2729 ],
2730 ..echo::Message::default()
2731 },
2732 &[],
2733 )
2734 .unwrap();
2735
2736 assert_eq!(response.data, Some(b"Shown".into()));
2737 }
2738
2739 #[test]
2740 fn nested_submsg() {
2741 let app = App::default();
2742
2743 let owner = Addr::unchecked("owner");
2744
2745 let contract_id = app.store_code(echo::contract());
2746 let contract = app
2747 .instantiate_contract(contract_id, owner.clone(), &EmptyMsg {}, &[], "Echo", None)
2748 .unwrap();
2749
2750 let response = app
2751 .execute_contract(
2752 owner,
2753 contract.clone(),
2754 &echo::Message {
2755 data: Some("Orig".to_owned()),
2756 sub_msg: vec![make_echo_submsg(
2757 contract.clone(),
2758 None,
2759 vec![make_echo_submsg(
2760 contract.clone(),
2761 "First",
2762 vec![make_echo_submsg(
2763 contract.clone(),
2764 "Second",
2765 vec![make_echo_submsg(
2766 contract,
2767 None,
2768 vec![],
2769 EXECUTE_REPLY_BASE_ID + 4,
2770 )],
2771 EXECUTE_REPLY_BASE_ID + 3,
2772 )],
2773 EXECUTE_REPLY_BASE_ID + 2,
2774 )],
2775 EXECUTE_REPLY_BASE_ID + 1,
2776 )],
2777 ..echo::Message::default()
2778 },
2779 &[],
2780 )
2781 .unwrap();
2782
2783 assert_eq!(response.data, Some(b"Second".into()));
2784 }
2785 }
2786
2787 mod response_validation {
2788 use super::*;
2789
2790 #[test]
2791 fn empty_attribute_key() {
2792 let app = App::default();
2793
2794 let owner = Addr::unchecked("owner");
2795
2796 let contract_id = app.store_code(echo::contract());
2797 let contract = app
2798 .instantiate_contract(contract_id, owner.clone(), &EmptyMsg {}, &[], "Echo", None)
2799 .unwrap();
2800
2801 let err = app
2802 .execute_contract(
2803 owner,
2804 contract,
2805 &echo::Message::<Empty> {
2806 data: None,
2807 attributes: vec![
2808 Attribute::new(" ", "value"),
2809 Attribute::new("proper", "proper_val"),
2810 ],
2811 ..echo::Message::default()
2812 },
2813 &[],
2814 )
2815 .unwrap_err();
2816
2817 assert_eq!(Error::empty_attribute_key("value"), err.downcast().unwrap(),);
2818 }
2819
2820 #[test]
2821 fn empty_attribute_value() {
2822 let app = App::default();
2823
2824 let owner = Addr::unchecked("owner");
2825
2826 let contract_id = app.store_code(echo::contract());
2827 let contract = app
2828 .instantiate_contract(contract_id, owner.clone(), &EmptyMsg {}, &[], "Echo", None)
2829 .unwrap();
2830
2831 let err = app
2832 .execute_contract(
2833 owner,
2834 contract,
2835 &echo::Message::<Empty> {
2836 data: None,
2837 attributes: vec![
2838 Attribute::new("key", " "),
2839 Attribute::new("proper", "proper_val"),
2840 ],
2841 ..echo::Message::default()
2842 },
2843 &[],
2844 )
2845 .unwrap_err();
2846
2847 assert_eq!(Error::empty_attribute_value("key"), err.downcast().unwrap());
2848 }
2849
2850 #[test]
2851 fn empty_event_attribute_key() {
2852 let app = App::default();
2853
2854 let owner = Addr::unchecked("owner");
2855
2856 let contract_id = app.store_code(echo::contract());
2857 let contract = app
2858 .instantiate_contract(contract_id, owner.clone(), &EmptyMsg {}, &[], "Echo", None)
2859 .unwrap();
2860
2861 let err = app
2862 .execute_contract(
2863 owner,
2864 contract,
2865 &echo::Message::<Empty> {
2866 data: None,
2867 events: vec![Event::new("event")
2868 .add_attribute(" ", "value")
2869 .add_attribute("proper", "proper_val")],
2870 ..echo::Message::default()
2871 },
2872 &[],
2873 )
2874 .unwrap_err();
2875
2876 assert_eq!(Error::empty_attribute_key("value"), err.downcast().unwrap());
2877 }
2878
2879 #[test]
2880 fn empty_event_attribute_value() {
2881 let app = App::default();
2882
2883 let owner = Addr::unchecked("owner");
2884
2885 let contract_id = app.store_code(echo::contract());
2886 let contract = app
2887 .instantiate_contract(contract_id, owner.clone(), &EmptyMsg {}, &[], "Echo", None)
2888 .unwrap();
2889
2890 let err = app
2891 .execute_contract(
2892 owner,
2893 contract,
2894 &echo::Message::<Empty> {
2895 data: None,
2896 events: vec![Event::new("event")
2897 .add_attribute("key", " ")
2898 .add_attribute("proper", "proper_val")],
2899 ..echo::Message::default()
2900 },
2901 &[],
2902 )
2903 .unwrap_err();
2904
2905 assert_eq!(Error::empty_attribute_value("key"), err.downcast().unwrap());
2906 }
2907
2908 #[test]
2909 fn too_short_event_type() {
2910 let app = App::default();
2911
2912 let owner = Addr::unchecked("owner");
2913
2914 let contract_id = app.store_code(echo::contract());
2915 let contract = app
2916 .instantiate_contract(contract_id, owner.clone(), &EmptyMsg {}, &[], "Echo", None)
2917 .unwrap();
2918
2919 let err = app
2920 .execute_contract(
2921 owner,
2922 contract,
2923 &echo::Message::<Empty> {
2924 data: None,
2925 events: vec![Event::new(" e "), Event::new("event")],
2926 ..echo::Message::default()
2927 },
2928 &[],
2929 )
2930 .unwrap_err();
2931
2932 assert_eq!(Error::event_type_too_short("e"), err.downcast().unwrap());
2933 }
2934 }
2935
2936 mod custom_messages {
2937 use super::*;
2938 use crate::custom_handler::CachingCustomHandler;
2939
2940 #[test]
2941 fn triggering_custom_msg() {
2942 let api = MockApi::default();
2943 let sender = api.addr_validate("sender").unwrap();
2944 let owner = api.addr_validate("owner").unwrap();
2945
2946 let custom_handler = CachingCustomHandler::<CustomMsg, Empty>::new();
2947 let custom_handler_state = custom_handler.state();
2948
2949 let app = AppBuilder::new_custom()
2950 .with_api(api)
2951 .with_custom(custom_handler)
2952 .build(no_init);
2953
2954 let contract_id = app.store_code(echo::custom_contract());
2955 let contract = app
2956 .instantiate_contract(contract_id, owner, &EmptyMsg {}, &[], "Echo", None)
2957 .unwrap();
2958
2959 app.execute_contract(
2960 sender,
2961 contract,
2962 &echo::Message {
2963 sub_msg: vec![SubMsg::new(CosmosMsg::Custom(CustomMsg::SetAge {
2964 age: 20,
2965 }))],
2966 ..Default::default()
2967 },
2968 &[],
2969 )
2970 .unwrap();
2971
2972 assert_eq!(
2973 custom_handler_state.execs().to_owned(),
2974 vec![CustomMsg::SetAge { age: 20 }]
2975 );
2976
2977 assert!(custom_handler_state.queries().is_empty());
2978 }
2979 }
2980
2981 mod protobuf_wrapped_data {
2982 use super::*;
2983 use crate::test_helpers::contracts::echo::EXECUTE_REPLY_BASE_ID;
2984 use cw_utils::parse_instantiate_response_data;
2985
2986 #[test]
2987 fn instantiate_wrapped_properly() {
2988 let owner = Addr::unchecked("owner");
2990 let init_funds = vec![coin(20, "btc")];
2991
2992 let app = custom_app::<CustomMsg, Empty, _>(|router, _, storage| {
2993 router
2994 .bank
2995 .init_balance(storage, &owner, init_funds)
2996 .unwrap();
2997 });
2998
2999 let code_id = app.store_code(reflect::contract());
3001 let init_msg = to_binary(&EmptyMsg {}).unwrap();
3002 let msg = WasmMsg::Instantiate {
3003 admin: None,
3004 code_id,
3005 msg: init_msg,
3006 funds: vec![],
3007 label: "label".into(),
3008 };
3009 let res = app.execute(owner, msg.into()).unwrap();
3010
3011 let parsed = parse_instantiate_response_data(res.data.unwrap().as_slice()).unwrap();
3013 assert!(parsed.data.is_none());
3014 let count: payout::CountResponse = app
3017 .wrap()
3018 .query_wasm_smart(&parsed.contract_address, &reflect::QueryMsg::Count {})
3019 .unwrap();
3020 assert_eq!(count.count, 0);
3021 }
3022
3023 #[test]
3024 fn instantiate_with_data_works() {
3025 let owner = Addr::unchecked("owner");
3026 let app = BasicApp::new(|_, _, _| {});
3027
3028 let code_id = app.store_code(echo::contract());
3030 let msg = echo::InitMessage::<Empty> {
3031 data: Some("food".into()),
3032 sub_msg: None,
3033 };
3034 let init_msg = to_binary(&msg).unwrap();
3035 let msg = WasmMsg::Instantiate {
3036 admin: None,
3037 code_id,
3038 msg: init_msg,
3039 funds: vec![],
3040 label: "label".into(),
3041 };
3042 let res = app.execute(owner, msg.into()).unwrap();
3043
3044 let parsed = parse_instantiate_response_data(res.data.unwrap().as_slice()).unwrap();
3046 assert!(parsed.data.is_some());
3047 assert_eq!(parsed.data.unwrap(), Binary::from(b"food"));
3048 assert!(!parsed.contract_address.is_empty());
3049 }
3050
3051 #[test]
3052 fn instantiate_with_reply_works() {
3053 let owner = Addr::unchecked("owner");
3054 let app = BasicApp::new(|_, _, _| {});
3055
3056 let code_id = app.store_code(echo::contract());
3058 let msg = echo::InitMessage::<Empty> {
3059 data: Some("food".into()),
3060 ..Default::default()
3061 };
3062 let addr1 = app
3063 .instantiate_contract(code_id, owner.clone(), &msg, &[], "first", None)
3064 .unwrap();
3065
3066 let msg = echo::Message::<Empty> {
3068 data: Some("Passed to contract instantiation, returned as reply, and then returned as response".into()),
3069 ..Default::default()
3070 };
3071 let sub_msg = SubMsg::reply_on_success(
3072 WasmMsg::Execute {
3073 contract_addr: addr1.to_string(),
3074 msg: to_binary(&msg).unwrap(),
3075 funds: vec![],
3076 },
3077 EXECUTE_REPLY_BASE_ID,
3078 );
3079 let init_msg = echo::InitMessage::<Empty> {
3080 data: Some("Overwrite me".into()),
3081 sub_msg: Some(vec![sub_msg]),
3082 };
3083 let init_msg = to_binary(&init_msg).unwrap();
3084 let msg = WasmMsg::Instantiate {
3085 admin: None,
3086 code_id,
3087 msg: init_msg,
3088 funds: vec![],
3089 label: "label".into(),
3090 };
3091 let res = app.execute(owner, msg.into()).unwrap();
3092
3093 let parsed = parse_instantiate_response_data(res.data.unwrap().as_slice()).unwrap();
3095 assert!(parsed.data.is_some());
3096 assert_eq!(parsed.data.unwrap(), Binary::from(b"Passed to contract instantiation, returned as reply, and then returned as response"));
3098 assert!(!parsed.contract_address.is_empty());
3099 assert_ne!(parsed.contract_address, addr1.to_string());
3100 }
3101
3102 #[test]
3103 fn execute_wrapped_properly() {
3104 let owner = Addr::unchecked("owner");
3105 let app = BasicApp::new(|_, _, _| {});
3106
3107 let code_id = app.store_code(echo::contract());
3109 let echo_addr = app
3110 .instantiate_contract(code_id, owner.clone(), &EmptyMsg {}, &[], "label", None)
3111 .unwrap();
3112
3113 let msg = echo::Message::<Empty> {
3115 data: Some("hello".into()),
3116 ..echo::Message::default()
3117 };
3118 let exec_res = app.execute_contract(owner, echo_addr, &msg, &[]).unwrap();
3120 assert_eq!(exec_res.data, Some(Binary::from(b"hello")));
3121 }
3122 }
3123
3124 mod errors {
3125 use super::*;
3126
3127 #[test]
3128 fn simple_instantiation() {
3129 let owner = Addr::unchecked("owner");
3130 let app = App::default();
3131
3132 let code_id = app.store_code(error::contract(false));
3134 let msg = EmptyMsg {};
3135 let err = app
3136 .instantiate_contract(code_id, owner, &msg, &[], "error", None)
3137 .unwrap_err();
3138
3139 let source: &StdError = err.downcast_ref().unwrap();
3141 if let StdError::GenericErr { msg } = source {
3142 assert_eq!(msg, "Init failed");
3143 } else {
3144 panic!("wrong StdError variant");
3145 }
3146
3147 assert_eq!(err.chain().count(), 2);
3150 }
3151
3152 #[test]
3153 fn simple_call() {
3154 let owner = Addr::unchecked("owner");
3155 let app = App::default();
3156
3157 let code_id = app.store_code(error::contract(true));
3159 let msg = EmptyMsg {};
3160 let contract_addr = app
3161 .instantiate_contract(code_id, owner, &msg, &[], "error", None)
3162 .unwrap();
3163
3164 let err = app
3166 .execute_contract(Addr::unchecked("random"), contract_addr, &msg, &[])
3167 .unwrap_err();
3168
3169 let source: &StdError = err.downcast_ref().unwrap();
3171 if let StdError::GenericErr { msg } = source {
3172 assert_eq!(msg, "Handle failed");
3173 } else {
3174 panic!("wrong StdError variant");
3175 }
3176
3177 assert_eq!(err.chain().count(), 2);
3180 }
3181
3182 #[test]
3183 fn nested_call() {
3184 let owner = Addr::unchecked("owner");
3185 let app = App::default();
3186
3187 let error_code_id = app.store_code(error::contract(true));
3188 let caller_code_id = app.store_code(caller::contract());
3189
3190 let msg = EmptyMsg {};
3192 let caller_addr = app
3193 .instantiate_contract(caller_code_id, owner.clone(), &msg, &[], "caller", None)
3194 .unwrap();
3195 let error_addr = app
3196 .instantiate_contract(error_code_id, owner, &msg, &[], "error", None)
3197 .unwrap();
3198
3199 let msg = WasmMsg::Execute {
3201 contract_addr: error_addr.into(),
3202 msg: to_binary(&EmptyMsg {}).unwrap(),
3203 funds: vec![],
3204 };
3205 let err = app
3206 .execute_contract(Addr::unchecked("random"), caller_addr, &msg, &[])
3207 .unwrap_err();
3208
3209 let source: &StdError = err.downcast_ref().unwrap();
3211 if let StdError::GenericErr { msg } = source {
3212 assert_eq!(msg, "Handle failed");
3213 } else {
3214 panic!("wrong StdError variant");
3215 }
3216
3217 assert_eq!(err.chain().count(), 3);
3220 }
3221
3222 #[test]
3223 fn double_nested_call() {
3224 let owner = Addr::unchecked("owner");
3225 let app = App::default();
3226
3227 let error_code_id = app.store_code(error::contract(true));
3228 let caller_code_id = app.store_code(caller::contract());
3229
3230 let msg = EmptyMsg {};
3232 let caller_addr1 = app
3233 .instantiate_contract(caller_code_id, owner.clone(), &msg, &[], "caller", None)
3234 .unwrap();
3235 let caller_addr2 = app
3236 .instantiate_contract(caller_code_id, owner.clone(), &msg, &[], "caller", None)
3237 .unwrap();
3238 let error_addr = app
3239 .instantiate_contract(error_code_id, owner, &msg, &[], "error", None)
3240 .unwrap();
3241
3242 let msg = WasmMsg::Execute {
3244 contract_addr: caller_addr2.into(),
3245 msg: to_binary(&WasmMsg::Execute {
3246 contract_addr: error_addr.into(),
3247 msg: to_binary(&EmptyMsg {}).unwrap(),
3248 funds: vec![],
3249 })
3250 .unwrap(),
3251 funds: vec![],
3252 };
3253 let err = app
3254 .execute_contract(Addr::unchecked("random"), caller_addr1, &msg, &[])
3255 .unwrap_err();
3256
3257 let source: &StdError = err.downcast_ref().unwrap();
3262 if let StdError::GenericErr { msg } = source {
3263 assert_eq!(msg, "Handle failed");
3264 } else {
3265 panic!("wrong StdError variant");
3266 }
3267
3268 assert_eq!(err.chain().count(), 4);
3271 }
3272 }
3273}