apollo_cw_multi_test/
app.rs

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
47/// Type alias for default build `App` to make its storing simpler in typical scenario
48pub 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
61/// Router is a persisted state. You can query this.
62/// Execution generally happens on the RouterCache, which then can be atomically committed or rolled back.
63/// We offer .execute() as a wrapper around cache, execute, commit/rollback process.
64pub 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    /// Creates new default `App` implementation working with Empty custom messages.
97    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
118/// Creates new default `App` implementation working with customized exec and query messages.
119/// Outside of `App` implementation to make type elision better.
120pub 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
198/// This is essential to create a custom app with custom handler.
199///   let mut app = BasicAppBuilder::<E, Q>::new_custom().with_custom(handler).build();
200pub 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
213/// Utility to build App in stages. If particular items wont be set, defaults would be used
214pub 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    /// Creates builder with default components working with empty exec and query messages.
262    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    /// Creates builder with default components designed to work with custom exec and query
297    /// messages.
298    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    /// Overwrites default wasm executor.
347    ///
348    /// At this point it is needed that new wasm implements some `Wasm` trait, but it doesn't need
349    /// to be bound to Bank or Custom yet - as those may change. The cross-components validation is
350    /// done on final building.
351    ///
352    /// Also it is possible to completely abandon trait bounding here which would not be bad idea,
353    /// however it might make the message on build creepy in many cases, so as for properly build
354    /// `App` we always want `Wasm` to be `Wasm`, some checks are done early.
355    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    /// Overwrites default bank interface
424    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    /// Overwrites default api interface
459    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    /// Overwrites default storage interface
494    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    /// Overwrites default custom messages handler
529    ///
530    /// At this point it is needed that new custom implements some `Module` trait, but it doesn't need
531    /// to be bound to ExecC or QueryC yet - as those may change. The cross-components validation is
532    /// done on final building.
533    ///
534    /// Also it is possible to completely abandon trait bounding here which would not be bad idea,
535    /// however it might make the message on build creepy in many cases, so as for properly build
536    /// `App` we always want `Wasm` to be `Wasm`, some checks are done early.
537    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    /// Overwrites default bank interface
572    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    /// Overwrites default distribution interface
607    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    /// Overwrites default ibc interface
652    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    /// Overwrites default gov interface
687    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    /// Overwrites default initial block
722    pub fn with_block(mut self, block: BlockInfo) -> Self {
723        self.block = block;
724        self
725    }
726
727    /// Builds final `App`. At this point all components type have to be properly related to each
728    /// other. If there are some generics related compilation error make sure, that all components
729    /// are properly relating to each other.
730    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
816// Helper functions to call some custom WasmKeeper logic.
817// They show how we can easily add such calls to other custom keepers (CustomT, StakingT, etc)
818impl<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    /// This registers contract code (like uploading wasm bytecode on a chain),
845    /// so it can later be used to instantiate a contract.
846    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    /// This allows to get `ContractData` for specific contract
851    pub fn contract_data(&self, address: &Addr) -> AnyResult<ContractData> {
852        self.read_module(|router, _, storage| router.wasm.load_contract(storage, address))
853    }
854
855    /// This gets a raw state dump of all key-values held by a given contract
856    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    // this let's use use "next block" steps that add eg. one height and 5 seconds
882    pub fn update_block<F: Fn(&mut BlockInfo)>(&self, action: F) {
883        action(self.block.borrow_mut().deref_mut());
884        // self.block.replace_with(action);
885    }
886
887    /// Returns a copy of the current block_info
888    pub fn block_info(&self) -> BlockInfo {
889        self.block.borrow().clone()
890    }
891
892    /// Simple helper so we get access to all the QuerierWrapper helpers,
893    /// eg. wrap().query_wasm_smart, query_all_balances, ...
894    pub fn wrap(&self) -> QuerierWrapper<CustomT::QueryT> {
895        QuerierWrapper::new(self)
896    }
897
898    /// Runs multiple CosmosMsg in one atomic operation.
899    /// This will create a cache before the execution, so no state changes are persisted if any of them
900    /// return an error. But all writes are persisted on success.
901    pub fn execute_multi(
902        &self,
903        sender: Addr,
904        msgs: Vec<cosmwasm_std::CosmosMsg<CustomT::ExecT>>,
905    ) -> AnyResult<Vec<AppResponse>> {
906        // we need to do some caching of storage here, once in the entry point:
907        // meaning, wrap current state, all writes go to a cache, only when execute
908        // returns a success do we flush it (otherwise drop it)
909
910        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    /// Call a smart contract in "sudo" mode.
933    /// This will create a cache before the execution, so no state changes are persisted if this
934    /// returns an error, but all are persisted on success.
935    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    /// Runs arbitrary SudoMsg.
962    /// This will create a cache before the execution, so no state changes are persisted if this
963    /// returns an error, but all are persisted on success.
964    pub fn sudo(&self, msg: SudoMsg) -> AnyResult<AppResponse> {
965        // we need to do some caching of storage here, once in the entry point:
966        // meaning, wrap current state, all writes go to a cache, only when execute
967        // returns a success do we flush it (otherwise drop it)
968        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    // this can remain crate-only as all special functions are wired up to app currently
985    // we need to figure out another format for wasm, as some like sudo need to be called after init
986    pub wasm: Wasm,
987    // these must be pub so we can initialize them (super user) on build
988    pub bank: Bank,
989    pub custom: Custom,
990    pub staking: Staking,
991    pub distribution: Distr,
992    pub ibc: Ibc,
993    pub gov: Gov,
994    // TODO: make stargate generic
995    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
1027/// We use it to allow calling into modules from another module in sudo mode.
1028/// Things like gov proposals belong here.
1029pub 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    /// this is used by `RouterQuerier` to actual implement the `Querier` interface.
1132    /// you most likely want to use `router.querier(storage, block).wrap()` to get a
1133    /// QuerierWrapper to interact with
1134    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                // Handle bank and wasm queries and pass the rest on to the stargate handler
1150                // If for any reason we want to support any of the other modules in App, e.g
1151                // staking, we need to add them here. This is because when you try to register
1152                // a module that is also part of the router, the router and the stargate module
1153                // will hold different versions of the same module, due to constraints in the
1154                // borrow system. This is not a problem if the module is state less, i.e. all
1155                // state is stored in the storage, like BankKeeper. But if the module is stateful,
1156                // like WasmKeeper then the codes HashMaps will end up out of sync. Therefore
1157                // decided to handle stargate messages for all internal modules here and only
1158                // passing the rest on to the stargate module.
1159                match path.as_str() {
1160                    // Bank queries
1161                    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                    // Wasm module queries
1219                    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                    // The rest of all stargate queries go to the stargate module
1255                    _ => 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            // initialization moved to App construction
1456            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        // send both tokens
1467        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        // can send from other account (but funds will be deducted from sender)
1480        app.execute(rcpt.clone(), msg).unwrap();
1481
1482        // cannot send too much
1483        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        // set personal balance
1497        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        // set up contract
1508        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        // sender funds deducted
1536        let sender = get_balance(&app, &owner);
1537        assert_eq!(sender, vec![coin(20, "btc"), coin(77, "eth")]);
1538        // get contract address, has funds
1539        let funds = get_balance(&app, &contract_addr);
1540        assert_eq!(funds, coins(23, "eth"));
1541
1542        // create empty account
1543        let random = Addr::unchecked("random");
1544        let funds = get_balance(&app, &random);
1545        assert_eq!(funds, vec![]);
1546
1547        // do one payout and see money coming in
1548        let res = app
1549            .execute_contract(random.clone(), contract_addr.clone(), &EmptyMsg {}, &[])
1550            .unwrap();
1551        assert_eq!(3, res.events.len());
1552
1553        // the call to payout does emit this as well as custom attributes
1554        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        // next is a custom wasm event
1559        let custom_attrs = res.custom_attrs(1);
1560        assert_eq!(custom_attrs, [("action", "payout")]);
1561
1562        // then the transfer event
1563        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        // random got cash
1570        let funds = get_balance(&app, &random);
1571        assert_eq!(funds, coins(5, "eth"));
1572        // contract lost it
1573        let funds = get_balance(&app, &contract_addr);
1574        assert_eq!(funds, coins(18, "eth"));
1575    }
1576
1577    #[test]
1578    fn reflect_success() {
1579        // set personal balance
1580        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        // set up payout contract
1591        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        // set up reflect contract
1607        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        // reflect account is empty
1613        let funds = get_balance(&app, &reflect_addr);
1614        assert_eq!(funds, vec![]);
1615        // reflect count is 1
1616        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        // reflecting payout message pays reflect contract
1623        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        // ensure the attributes were relayed from the sub-message
1636        assert_eq!(4, res.events.len(), "{:?}", res.events);
1637
1638        // reflect only returns standard wasm-execute event
1639        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        // the call to payout does emit this as well as custom attributes
1644        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        // final event is the transfer from bank
1659        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        // ensure transfer was executed with reflect as sender
1667        let funds = get_balance(&app, &reflect_addr);
1668        assert_eq!(funds, coins(5, "eth"));
1669
1670        // reflect count updated
1671        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        // set personal balance
1681        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        // set up reflect contract
1692        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        // reflect has 40 eth
1705        let funds = get_balance(&app, &reflect_addr);
1706        assert_eq!(funds, coins(40, "eth"));
1707        let random = Addr::unchecked("random");
1708
1709        // sending 7 eth works
1710        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        // no wasm events as no attributes
1721        assert_eq!(2, res.events.len());
1722        // standard wasm-execute event
1723        let exec = &res.events[0];
1724        assert_eq!(exec.ty.as_str(), "execute");
1725        assert_eq!(exec.attributes, [("_contract_addr", &reflect_addr)]);
1726        // only transfer event from bank
1727        let transfer = &res.events[1];
1728        assert_eq!(transfer.ty.as_str(), "transfer");
1729
1730        // ensure random got paid
1731        let funds = get_balance(&app, &random);
1732        assert_eq!(funds, coins(7, "eth"));
1733
1734        // reflect count should be updated to 1
1735        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        // sending 8 eth, then 3 btc should fail both
1742        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        // first one should have been rolled-back on error (no second payment)
1762        let funds = get_balance(&app, &random);
1763        assert_eq!(funds, coins(7, "eth"));
1764
1765        // failure should not update reflect count
1766        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        // count is 1
1794        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        // wasm_sudo call
1801        let msg = payout::SudoMsg { set_count: 25 };
1802        app.wasm_sudo(payout_addr.clone(), &msg).unwrap();
1803
1804        // count is 25
1805        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        // we can do the same with sudo call
1812        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    // this demonstrates that we can mint tokens and send from other accounts via a custom module,
1827    // as an example of ability to do privileged actions
1828    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            // we mint LOTTERY tokens to this one
1843            lucky_winner: String,
1844            // we transfer PITY from lucky_winner to runner_up
1845            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                // mint new tokens
1873                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                // send from an arbitrary account (not the module)
1880                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            // this is a custom initialization method
1920            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        // let's call this custom handler
1933        #[test]
1934        fn dispatches_messages() {
1935            let winner = "winner".to_string();
1936            let second = "second".to_string();
1937
1938            // payments. note 54321 - 12321 = 42000
1939            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            // query that balances are empty
1953            let start = app.wrap().query_balance(&winner, denom).unwrap();
1954            assert_eq!(start, coin(0, denom));
1955
1956            // trigger the custom module
1957            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            // see if coins were properly added
1964            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        // set personal balance
1974        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        // set up reflect contract
1986        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        // no reply writen beforehand
1999        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        // reflect sends 7 eth, success
2004        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        // expected events: execute, transfer, reply, custom wasm (set in reply)
2019        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        // ensure success was written
2030        let res: Reply = app.wrap().query_wasm_smart(&reflect_addr, &query).unwrap();
2031        assert_eq!(res.id, 123);
2032        // validate the events written in the reply blob...should just be bank transfer
2033        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        // reflect sends 300 btc, failure, but error caught by submessage (so shows success)
2039        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        // ensure error was written
2054        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        // TODO: check error?
2059    }
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        // set personal balance
2119        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        // cache 1 - send some tokens
2131        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        // shows up in cache
2149        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        // now, second level cache
2155        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            // shows up in 2nd cache
2166            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        // apply first to router
2175        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        // Testing if funds on contract are properly visible on contract.
2184        // Hackatom contract is initialized with 10btc. Then, the contract is executed, with
2185        // additional 20btc. Then beneficiary balance is checked - expeced value is 30btc. 10btc
2186        // would mean that sending tokens with message is not visible for this very message, and
2187        // 20btc means, that only such just send funds are visible.
2188        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        // Check balance of all accounts to ensure no tokens where burned or created, and they are
2222        // in correct places
2223        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        // The plan:
2231        // create a hackatom contract with some funds
2232        // check admin set properly
2233        // check beneficiary set properly
2234        // migrate fails if not admin
2235        // migrate succeeds if admin
2236        // check beneficiary updated
2237        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        // create a hackatom contract with some funds
2249        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        // check admin set properly
2264        let info = app.contract_data(&contract).unwrap();
2265        assert_eq!(info.admin, Some(owner.clone()));
2266        // check beneficiary set properly
2267        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        // migrate fails if not admin
2274        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        // migrate fails if unregistred code id
2282        app.migrate_contract(
2283            owner.clone(),
2284            contract.clone(),
2285            &migrate_msg,
2286            contract_id + 7,
2287        )
2288        .unwrap_err();
2289
2290        // migrate succeeds when the stars align
2291        app.migrate_contract(owner, contract.clone(), &migrate_msg, contract_id)
2292            .unwrap();
2293
2294        // check beneficiary updated
2295        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        // The plan:
2305        // create a hackatom contract
2306        // check admin set properly
2307        // update admin succeeds if admin
2308        // update admin fails if not (new) admin
2309        // check admin set properly
2310        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        // create a hackatom contract with some funds
2317        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        // check admin set properly
2332        let info = app.contract_data(&contract).unwrap();
2333        assert_eq!(info.admin, Some(owner.clone()));
2334
2335        // transfer adminship to owner2
2336        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        // check admin set properly
2346        let info = app.contract_data(&contract).unwrap();
2347        assert_eq!(info.admin, Some(owner2.clone()));
2348
2349        // update admin fails if not owner2
2350        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        // check admin still the same
2360        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            // set personal balance
2555            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            // set up reflect contract
2566            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            // set up echo contract
2579            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            // reflect will call echo
2585            // echo will set the data
2586            // top-level app will not display the data
2587            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            // ensure data is empty
2605            assert_eq!(res.data, None);
2606            // ensure expected events
2607            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            // set personal balance
2989            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            // set up reflect contract
3000            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            // assert we have a proper instantiate result
3012            let parsed = parse_instantiate_response_data(res.data.unwrap().as_slice()).unwrap();
3013            assert!(parsed.data.is_none());
3014            // check the address is right
3015
3016            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            // set up echo contract
3029            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            // assert we have a proper instantiate result
3045            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            // set up echo contract
3057            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            // another echo contract
3067            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            // assert we have a proper instantiate result
3094            let parsed = parse_instantiate_response_data(res.data.unwrap().as_slice()).unwrap();
3095            assert!(parsed.data.is_some());
3096            // Result is from the reply, not the original one
3097            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            // set up reflect contract
3108            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            // ensure the execute has the same wrapper as it should
3114            let msg = echo::Message::<Empty> {
3115                data: Some("hello".into()),
3116                ..echo::Message::default()
3117            };
3118            // execute_contract now decodes a protobuf wrapper, so we get the top-level response
3119            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            // set up contract
3133            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            // we should be able to retrieve the original error by downcasting
3140            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            // We're expecting exactly 2 nested error types
3148            // (the original error, WasmMsg context)
3149            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            // set up contract
3158            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            // execute should error
3165            let err = app
3166                .execute_contract(Addr::unchecked("random"), contract_addr, &msg, &[])
3167                .unwrap_err();
3168
3169            // we should be able to retrieve the original error by downcasting
3170            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            // We're expecting exactly 2 nested error types
3178            // (the original error, WasmMsg context)
3179            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            // set up contracts
3191            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            // execute should error
3200            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            // we can downcast to get the original error
3210            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            // We're expecting exactly 3 nested error types
3218            // (the original error, 2 WasmMsg contexts)
3219            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            // set up contracts
3231            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            // caller1 calls caller2, caller2 calls error
3243            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            // uncomment to have the test fail and see how the error stringifies
3258            // panic!("{:?}", err);
3259
3260            // we can downcast to get the original error
3261            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            // We're expecting exactly 4 nested error types
3269            // (the original error, 3 WasmMsg contexts)
3270            assert_eq!(err.chain().count(), 4);
3271        }
3272    }
3273}