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