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