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 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 reply = Reply {
876 id,
877 payload,
878 gas_used: 0,
879 result: SubMsgResult::Ok(
880 #[allow(deprecated)]
881 SubMsgResponse {
882 events: r.events.clone(),
883 data: r.data.clone(),
884 msg_responses: vec![MsgResponse {
885 type_url,
886 value: r.data.unwrap_or_default(),
887 }],
888 },
889 ),
890 };
891 let reply_res = self.reply(api, router, storage, block, contract, reply)?;
893 r.data = reply_res.data;
895 r.events.extend_from_slice(&reply_res.events);
897 } else {
898 r.data = None;
900 }
901 Ok(r)
902 } else if let Err(e) = sub_message_result {
903 if matches!(reply_on, ReplyOn::Always | ReplyOn::Error) {
904 let reply = Reply {
905 id,
906 payload,
907 gas_used: 0,
908 result: SubMsgResult::Err(format!("{e:?}")),
909 };
910 self.reply(api, router, storage, block, contract, reply)
911 } else {
912 Err(e)
913 }
914 } else {
915 sub_message_result
916 }
917 }
918
919 fn reply(
920 &self,
921 api: &dyn Api,
922 router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
923 storage: &mut dyn Storage,
924 block: &BlockInfo,
925 contract: Addr,
926 reply: Reply,
927 ) -> AnyResult<AppResponse> {
928 let ok_attr = if reply.result.is_ok() {
929 "handle_success"
930 } else {
931 "handle_failure"
932 };
933 let custom_event = Event::new("reply")
934 .add_attribute(CONTRACT_ATTR, &contract)
935 .add_attribute("mode", ok_attr);
936
937 let res = self.call_reply(contract.clone(), api, storage, router, block, reply)?;
938 let (res, msgs) = self.build_app_response(&contract, custom_event, res);
939 self.process_response(api, router, storage, block, contract, res, msgs)
940 }
941
942 fn build_app_response(
946 &self,
947 contract: &Addr,
948 custom_event: Event, response: Response<ExecC>,
950 ) -> (AppResponse, Vec<SubMsg<ExecC>>) {
951 let Response {
952 messages,
953 attributes,
954 events,
955 data,
956 ..
957 } = response;
958
959 let mut app_events = Vec::with_capacity(2 + events.len());
961 app_events.push(custom_event);
962
963 if !attributes.is_empty() {
965 let wasm_event = Event::new("wasm")
967 .add_attribute(CONTRACT_ATTR, contract)
968 .add_attributes(attributes);
969 app_events.push(wasm_event);
970 }
971
972 let wasm_events = events.into_iter().map(|mut ev| {
975 ev.ty = format!("wasm-{}", ev.ty);
976 ev.attributes
977 .insert(0, mock_wasmd_attr(CONTRACT_ATTR, contract));
978 ev
979 });
980 app_events.extend(wasm_events);
981
982 let app_response = AppResponse {
983 events: app_events,
984 data,
985 };
986 (app_response, messages)
987 }
988
989 fn process_response(
990 &self,
991 api: &dyn Api,
992 router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
993 storage: &mut dyn Storage,
994 block: &BlockInfo,
995 contract: Addr,
996 response: AppResponse,
997 sub_messages: Vec<SubMsg<ExecC>>,
998 ) -> AnyResult<AppResponse> {
999 let AppResponse {
1001 mut events, data, ..
1002 } = response;
1003 let data = sub_messages
1005 .into_iter()
1006 .try_fold(data, |data, sub_message| {
1007 let sub_response = self.execute_submsg(
1009 api,
1010 router,
1011 storage,
1012 block,
1013 contract.clone(),
1014 sub_message,
1015 )?;
1016 events.extend_from_slice(&sub_response.events);
1018 Ok::<_, AnyError>(sub_response.data.or(data))
1020 })?;
1021 Ok(AppResponse { events, data })
1025 }
1026
1027 pub fn register_contract(
1033 &self,
1034 api: &dyn Api,
1035 storage: &mut dyn Storage,
1036 code_id: u64,
1037 creator: Addr,
1038 admin: impl Into<Option<Addr>>,
1039 label: String,
1040 created: u64,
1041 salt: impl Into<Option<Binary>>,
1042 ) -> AnyResult<Addr> {
1043 if code_id as usize > self.code_data.len() {
1045 bail!("Cannot init contract with unregistered code id");
1046 }
1047
1048 let instance_id = self.instance_count(storage) as u64;
1050 let addr = if let Some(salt_binary) = salt.into() {
1051 let code_data = self.code_data(code_id)?;
1053 let canonical_addr = &api.addr_canonicalize(creator.as_ref())?;
1054 self.address_generator.predictable_contract_address(
1055 api,
1056 storage,
1057 code_id,
1058 instance_id,
1059 code_data.checksum.as_slice(),
1060 canonical_addr,
1061 salt_binary.as_slice(),
1062 )?
1063 } else {
1064 self.address_generator
1066 .contract_address(api, storage, code_id, instance_id)?
1067 };
1068
1069 if self.contract_data(storage, &addr).is_ok() {
1071 bail!(Error::duplicated_contract_address(addr));
1072 }
1073
1074 let info = ContractData {
1076 code_id,
1077 creator,
1078 admin: admin.into(),
1079 label,
1080 created,
1081 };
1082 self.save_contract(storage, &addr, &info)?;
1083 Ok(addr)
1084 }
1085
1086 pub fn call_execute(
1088 &self,
1089 api: &dyn Api,
1090 storage: &mut dyn Storage,
1091 address: Addr,
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.execute(deps, env, info, msg),
1104 )?)
1105 }
1106
1107 pub fn call_instantiate(
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 info: MessageInfo,
1116 msg: Vec<u8>,
1117 ) -> AnyResult<Response<ExecC>> {
1118 Self::verify_response(self.with_storage(
1119 api,
1120 storage,
1121 router,
1122 block,
1123 address,
1124 |contract, deps, env| contract.instantiate(deps, env, info, msg),
1125 )?)
1126 }
1127
1128 pub fn call_reply(
1130 &self,
1131 address: Addr,
1132 api: &dyn Api,
1133 storage: &mut dyn Storage,
1134 router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
1135 block: &BlockInfo,
1136 reply: Reply,
1137 ) -> AnyResult<Response<ExecC>> {
1138 Self::verify_response(self.with_storage(
1139 api,
1140 storage,
1141 router,
1142 block,
1143 address,
1144 |contract, deps, env| contract.reply(deps, env, reply),
1145 )?)
1146 }
1147
1148 pub fn call_sudo(
1150 &self,
1151 address: Addr,
1152 api: &dyn Api,
1153 storage: &mut dyn Storage,
1154 router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
1155 block: &BlockInfo,
1156 msg: Vec<u8>,
1157 ) -> AnyResult<Response<ExecC>> {
1158 Self::verify_response(self.with_storage(
1159 api,
1160 storage,
1161 router,
1162 block,
1163 address,
1164 |contract, deps, env| contract.sudo(deps, env, msg),
1165 )?)
1166 }
1167
1168 #[cfg(not(feature = "cosmwasm_2_2"))]
1170 pub fn call_migrate(
1171 &self,
1172 address: Addr,
1173 api: &dyn Api,
1174 storage: &mut dyn Storage,
1175 router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
1176 block: &BlockInfo,
1177 msg: Vec<u8>,
1178 ) -> AnyResult<Response<ExecC>> {
1179 Self::verify_response(self.with_storage(
1180 api,
1181 storage,
1182 router,
1183 block,
1184 address,
1185 |contract, deps, env| contract.migrate(deps, env, msg),
1186 )?)
1187 }
1188
1189 #[cfg(feature = "cosmwasm_2_2")]
1191 pub fn call_migrate(
1192 &self,
1193 address: Addr,
1194 api: &dyn Api,
1195 storage: &mut dyn Storage,
1196 router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
1197 block: &BlockInfo,
1198 msg: Vec<u8>,
1199 info: MigrateInfo,
1200 ) -> AnyResult<Response<ExecC>> {
1201 Self::verify_response(self.with_storage(
1202 api,
1203 storage,
1204 router,
1205 block,
1206 address,
1207 |contract, deps, env| contract.migrate(deps, env, msg, info),
1208 )?)
1209 }
1210
1211 fn get_env<T: Into<Addr>>(&self, address: T, block: &BlockInfo) -> Env {
1212 Env {
1213 block: block.clone(),
1214 contract: ContractInfo {
1215 address: address.into(),
1216 },
1217 transaction: Some(TransactionInfo { index: 0 }),
1218 }
1219 }
1220
1221 fn with_storage_readonly<F, T>(
1222 &self,
1223 api: &dyn Api,
1224 storage: &dyn Storage,
1225 querier: &dyn Querier,
1226 block: &BlockInfo,
1227 address: Addr,
1228 action: F,
1229 ) -> AnyResult<T>
1230 where
1231 F: FnOnce(&dyn Contract<ExecC, QueryC>, Deps<QueryC>, Env) -> AnyResult<T>,
1232 {
1233 let contract = self.contract_data(storage, &address)?;
1234 let handler = self.contract_code(contract.code_id)?;
1235 let storage = self.contract_storage(storage, &address);
1236 let env = self.get_env(address, block);
1237
1238 let deps = Deps {
1239 storage: storage.as_ref(),
1240 api,
1241 querier: QuerierWrapper::new(querier),
1242 };
1243 action(handler, deps, env)
1244 }
1245
1246 fn with_storage<F, T>(
1247 &self,
1248 api: &dyn Api,
1249 storage: &mut dyn Storage,
1250 router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
1251 block: &BlockInfo,
1252 address: Addr,
1253 action: F,
1254 ) -> AnyResult<T>
1255 where
1256 F: FnOnce(&dyn Contract<ExecC, QueryC>, DepsMut<QueryC>, Env) -> AnyResult<T>,
1257 ExecC: DeserializeOwned,
1258 {
1259 let contract = self.contract_data(storage, &address)?;
1260 let handler = self.contract_code(contract.code_id)?;
1261
1262 transactional(storage, |write_cache, read_store| {
1267 let mut contract_storage = self.contract_storage_mut(write_cache, &address);
1268 let querier = RouterQuerier::new(router, api, read_store, block);
1269 let env = self.get_env(address, block);
1270
1271 let deps = DepsMut {
1272 storage: contract_storage.as_mut(),
1273 api,
1274 querier: QuerierWrapper::new(&querier),
1275 };
1276 action(handler, deps, env)
1277 })
1278 }
1279
1280 pub fn save_contract(
1282 &self,
1283 storage: &mut dyn Storage,
1284 address: &Addr,
1285 contract: &ContractData,
1286 ) -> AnyResult<()> {
1287 let mut storage: TypedPrefixedStorageMut<'_, WasmKeeper<ExecC, QueryC>> =
1288 WasmStorageMut::new(storage);
1289 CONTRACTS
1290 .save(&mut storage, address, contract)
1291 .map_err(Into::into)
1292 }
1293
1294 fn instance_count(&self, storage: &dyn Storage) -> usize {
1296 let storage: TypedPrefixedStorage<'_, WasmKeeper<ExecC, QueryC>> =
1297 WasmStorage::new(storage);
1298 CONTRACTS
1299 .range_raw(&storage, None, None, Order::Ascending)
1300 .count()
1301 }
1302
1303 fn response_type_url(msg: &CosmosMsg<ExecC>) -> String {
1305 const UNKNOWN: &str = "/unknown";
1306 match &msg {
1307 CosmosMsg::Bank(bank_msg) => match bank_msg {
1308 BankMsg::Send { .. } => "/cosmos.bank.v1beta1.MsgSendResponse",
1309 BankMsg::Burn { .. } => "/cosmos.bank.v1beta1.MsgBurnResponse",
1310 _ => UNKNOWN,
1311 },
1312 CosmosMsg::Custom(..) => UNKNOWN,
1313 #[cfg(feature = "staking")]
1314 CosmosMsg::Staking(staking_msg) => match staking_msg {
1315 StakingMsg::Delegate { .. } => "/cosmos.staking.v1beta1.MsgDelegateResponse",
1316 StakingMsg::Undelegate { .. } => "/cosmos.staking.v1beta1.MsgUndelegateResponse",
1317 StakingMsg::Redelegate { .. } => {
1318 "/cosmos.staking.v1beta1.MsgBeginRedelegateResponse"
1319 }
1320 _ => UNKNOWN,
1321 },
1322 #[cfg(feature = "staking")]
1323 CosmosMsg::Distribution(distribution_msg) => match distribution_msg {
1324 #[cfg(feature = "cosmwasm_1_3")]
1325 DistributionMsg::FundCommunityPool { .. } => {
1326 "/cosmos.distribution.v1beta1.MsgFundCommunityPoolResponse"
1327 }
1328 DistributionMsg::SetWithdrawAddress { .. } => {
1329 "/cosmos.distribution.v1beta1.MsgSetWithdrawAddressResponse"
1330 }
1331 DistributionMsg::WithdrawDelegatorReward { .. } => {
1332 "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorRewardResponse"
1333 }
1334 _ => UNKNOWN,
1335 },
1336 #[cfg(feature = "stargate")]
1337 #[allow(deprecated)]
1338 CosmosMsg::Stargate { .. } => UNKNOWN,
1339 #[cfg(feature = "cosmwasm_2_0")]
1340 CosmosMsg::Any(..) => UNKNOWN,
1341 #[cfg(feature = "stargate")]
1342 CosmosMsg::Ibc(..) => UNKNOWN,
1343 CosmosMsg::Wasm(wasm_msg) => match wasm_msg {
1344 WasmMsg::Instantiate { .. } => "/cosmwasm.wasm.v1.MsgInstantiateContractResponse",
1345 #[cfg(feature = "cosmwasm_1_2")]
1346 WasmMsg::Instantiate2 { .. } => "/cosmwasm.wasm.v1.MsgInstantiateContract2Response",
1347 WasmMsg::Execute { .. } => "/cosmwasm.wasm.v1.MsgExecuteContractResponse",
1348 WasmMsg::Migrate { .. } => "/cosmwasm.wasm.v1.MsgMigrateContractResponse",
1349 WasmMsg::UpdateAdmin { .. } => "/cosmwasm.wasm.v1.MsgUpdateAdminResponse",
1350 WasmMsg::ClearAdmin { .. } => "/cosmwasm.wasm.v1.MsgClearAdminResponse",
1351 _ => UNKNOWN,
1352 },
1353 #[cfg(feature = "stargate")]
1354 CosmosMsg::Gov(gov_msg) => match gov_msg {
1355 GovMsg::Vote { .. } => "/cosmos.gov.v1beta1.MsgVoteResponse",
1356 #[cfg(feature = "cosmwasm_1_2")]
1357 GovMsg::VoteWeighted { .. } => "/cosmos.gov.v1beta1.MsgVoteWeightedResponse",
1358 },
1359 _ => UNKNOWN,
1360 }
1361 .to_string()
1362 }
1363}
1364
1365#[derive(Clone, PartialEq, Message)]
1366struct InstantiateResponse {
1367 #[prost(string, tag = "1")]
1368 pub address: String,
1369 #[prost(bytes, tag = "2")]
1370 pub data: Vec<u8>,
1371}
1372
1373fn instantiate_response(data: Option<Binary>, contact_address: &Addr) -> Binary {
1374 let data = data.unwrap_or_default().to_vec();
1375 let init_data = InstantiateResponse {
1376 address: contact_address.into(),
1377 data,
1378 };
1379 let mut new_data = Vec::<u8>::with_capacity(init_data.encoded_len());
1380 init_data.encode(&mut new_data).unwrap();
1382 new_data.into()
1383}
1384
1385#[derive(Clone, PartialEq, Message)]
1386struct ExecuteResponse {
1387 #[prost(bytes, tag = "1")]
1388 pub data: Vec<u8>,
1389}
1390
1391fn encode_response_data(data: Option<Binary>) -> Option<Binary> {
1393 data.map(|d| {
1394 let execute_response = ExecuteResponse { data: d.to_vec() };
1395 let mut encoded_data = Vec::<u8>::with_capacity(execute_response.encoded_len());
1396 execute_response.encode(&mut encoded_data).unwrap();
1397 encoded_data.into()
1398 })
1399}
1400
1401#[cfg(test)]
1402mod test {
1403 use super::*;
1404 use crate::app::Router;
1405 use crate::bank::BankKeeper;
1406 use crate::featured::staking::{DistributionKeeper, StakeKeeper};
1407 use crate::module::FailingModule;
1408 use crate::test_helpers::{caller, error, payout};
1409 use crate::transactions::StorageTransaction;
1410 use crate::{GovFailingModule, IbcFailingModule, StargateFailing};
1411 use cosmwasm_std::testing::{message_info, mock_env, MockApi, MockQuerier, MockStorage};
1412 #[cfg(feature = "cosmwasm_1_2")]
1413 use cosmwasm_std::CodeInfoResponse;
1414 use cosmwasm_std::{
1415 coin, from_json, to_json_vec, CanonicalAddr, CosmosMsg, Empty, HexBinary, StdError,
1416 };
1417
1418 type BasicRouter<ExecC = Empty, QueryC = Empty> = Router<
1420 BankKeeper,
1421 FailingModule<ExecC, QueryC, Empty>,
1422 WasmKeeper<ExecC, QueryC>,
1423 StakeKeeper,
1424 DistributionKeeper,
1425 IbcFailingModule,
1426 GovFailingModule,
1427 StargateFailing,
1428 >;
1429
1430 fn wasm_keeper() -> WasmKeeper<Empty, Empty> {
1431 WasmKeeper::new()
1432 }
1433
1434 fn mock_router() -> BasicRouter {
1435 Router {
1436 wasm: WasmKeeper::new(),
1437 bank: BankKeeper::new(),
1438 custom: FailingModule::new(),
1439 staking: StakeKeeper::new(),
1440 distribution: DistributionKeeper::new(),
1441 ibc: IbcFailingModule::new(),
1442 gov: GovFailingModule::new(),
1443 stargate: StargateFailing,
1444 }
1445 }
1446
1447 #[test]
1448 fn register_contract() {
1449 let api = MockApi::default();
1450
1451 let creator_addr = api.addr_make("creator");
1453 let user_addr = api.addr_make("foobar");
1454 let admin_addr = api.addr_make("admin");
1455 let unregistered_addr = api.addr_make("unregistered");
1456
1457 let mut wasm_storage = MockStorage::new();
1458 let mut wasm_keeper = wasm_keeper();
1459 let block = mock_env().block;
1460 let code_id = wasm_keeper.store_code(creator_addr, error::contract(false));
1461
1462 transactional(&mut wasm_storage, |cache, _| {
1463 wasm_keeper.register_contract(
1465 &api,
1466 cache,
1467 code_id + 1,
1468 user_addr.clone(),
1469 admin_addr.clone(),
1470 "label".to_owned(),
1471 1000,
1472 None,
1473 )
1474 })
1475 .unwrap_err();
1476
1477 let contract_addr = transactional(&mut wasm_storage, |cache, _| {
1478 wasm_keeper.register_contract(
1480 &api,
1481 cache,
1482 code_id,
1483 user_addr.clone(),
1484 admin_addr.clone(),
1485 "label".to_owned(),
1486 1000,
1487 None,
1488 )
1489 })
1490 .unwrap();
1491
1492 let contract_data = wasm_keeper
1494 .contract_data(&wasm_storage, &contract_addr)
1495 .unwrap();
1496
1497 assert_eq!(
1498 contract_data,
1499 ContractData {
1500 code_id,
1501 creator: user_addr.clone(),
1502 admin: admin_addr.into(),
1503 label: "label".to_owned(),
1504 created: 1000,
1505 }
1506 );
1507
1508 let err = transactional(&mut wasm_storage, |cache, _| {
1509 let info = message_info(&user_addr, &[]);
1511 wasm_keeper.call_instantiate(
1512 contract_addr.clone(),
1513 &api,
1514 cache,
1515 &mock_router(),
1516 &block,
1517 info,
1518 b"{}".to_vec(),
1519 )
1520 })
1521 .unwrap_err();
1522
1523 assert_eq!(
1525 StdError::generic_err("Init failed"),
1526 err.downcast().unwrap()
1527 );
1528
1529 let err = transactional(&mut wasm_storage, |cache, _| {
1530 let info = message_info(&user_addr, &[]);
1532 wasm_keeper.call_instantiate(
1533 unregistered_addr,
1534 &api,
1535 cache,
1536 &mock_router(),
1537 &block,
1538 info,
1539 b"{}".to_vec(),
1540 )
1541 })
1542 .unwrap_err();
1543
1544 assert!(matches!(err.downcast().unwrap(), StdError::NotFound { .. }));
1546 }
1547
1548 #[test]
1549 fn query_contract_info() {
1550 let api = MockApi::default();
1551
1552 let creator_addr = api.addr_make("creator");
1554 let admin_addr = api.addr_make("admin");
1555
1556 let mut wasm_storage = MockStorage::new();
1557 let mut wasm_keeper = wasm_keeper();
1558 let block = mock_env().block;
1559 let code_id = wasm_keeper.store_code(creator_addr.clone(), payout::contract());
1560 assert_eq!(1, code_id);
1561
1562 let contract_addr = wasm_keeper
1563 .register_contract(
1564 &api,
1565 &mut wasm_storage,
1566 code_id,
1567 creator_addr.clone(),
1568 admin_addr.clone(),
1569 "label".to_owned(),
1570 1000,
1571 None,
1572 )
1573 .unwrap();
1574
1575 let querier: MockQuerier<Empty> = MockQuerier::new(&[]);
1576 let query = WasmQuery::ContractInfo {
1577 contract_addr: contract_addr.into(),
1578 };
1579
1580 let contract_info = wasm_keeper
1581 .query(&api, &wasm_storage, &querier, &block, query)
1582 .unwrap();
1583
1584 let actual: ContractInfoResponse = from_json(contract_info).unwrap();
1585 let expected =
1586 ContractInfoResponse::new(code_id, creator_addr, admin_addr.into(), false, None);
1587 assert_eq!(expected, actual);
1588 }
1589
1590 #[test]
1591 #[cfg(feature = "cosmwasm_1_2")]
1592 fn query_code_info() {
1593 let api = MockApi::default();
1594 let wasm_storage = MockStorage::new();
1595 let mut wasm_keeper = wasm_keeper();
1596 let block = mock_env().block;
1597 let creator_addr = api.addr_make("creator");
1598 let code_id = wasm_keeper.store_code(creator_addr.clone(), payout::contract());
1599 let querier: MockQuerier<Empty> = MockQuerier::new(&[]);
1600 let query = WasmQuery::CodeInfo { code_id };
1601 let code_info = wasm_keeper
1602 .query(&api, &wasm_storage, &querier, &block, query)
1603 .unwrap();
1604 let actual: CodeInfoResponse = from_json(code_info).unwrap();
1605 assert_eq!(code_id, actual.code_id);
1606 assert_eq!(creator_addr.as_str(), actual.creator.as_str());
1607 assert_eq!(32, actual.checksum.as_slice().len());
1608 }
1609
1610 #[test]
1611 #[cfg(feature = "cosmwasm_1_2")]
1612 fn different_contracts_must_have_different_checksum() {
1613 let api = MockApi::default();
1614 let creator_addr = api.addr_make("creator");
1615 let wasm_storage = MockStorage::new();
1616 let mut wasm_keeper = wasm_keeper();
1617 let block = mock_env().block;
1618 let code_id_payout = wasm_keeper.store_code(creator_addr.clone(), payout::contract());
1619 let code_id_caller = wasm_keeper.store_code(creator_addr, caller::contract());
1620 let querier: MockQuerier<Empty> = MockQuerier::new(&[]);
1621 let query_payout = WasmQuery::CodeInfo {
1622 code_id: code_id_payout,
1623 };
1624 let query_caller = WasmQuery::CodeInfo {
1625 code_id: code_id_caller,
1626 };
1627 let code_info_payout = wasm_keeper
1628 .query(&api, &wasm_storage, &querier, &block, query_payout)
1629 .unwrap();
1630 let code_info_caller = wasm_keeper
1631 .query(&api, &wasm_storage, &querier, &block, query_caller)
1632 .unwrap();
1633 let info_payout: CodeInfoResponse = from_json(code_info_payout).unwrap();
1634 let info_caller: CodeInfoResponse = from_json(code_info_caller).unwrap();
1635 assert_eq!(code_id_payout, info_payout.code_id);
1636 assert_eq!(code_id_caller, info_caller.code_id);
1637 assert_ne!(info_caller.code_id, info_payout.code_id);
1638 assert_eq!(info_caller.creator, info_payout.creator);
1639 assert_ne!(info_caller.checksum, info_payout.checksum);
1640 }
1641
1642 #[test]
1643 #[cfg(feature = "cosmwasm_1_2")]
1644 fn querying_invalid_code_info_must_fail() {
1645 let api = MockApi::default();
1646 let wasm_storage = MockStorage::new();
1647 let wasm_keeper = wasm_keeper();
1648 let block = mock_env().block;
1649
1650 let querier: MockQuerier<Empty> = MockQuerier::new(&[]);
1651 let query = WasmQuery::CodeInfo { code_id: 100 };
1652
1653 wasm_keeper
1654 .query(&api, &wasm_storage, &querier, &block, query)
1655 .unwrap_err();
1656 }
1657
1658 #[test]
1659 fn can_dump_raw_wasm_state() {
1660 let api = MockApi::default();
1661
1662 let creator_addr = api.addr_make("creator");
1664 let admin_addr = api.addr_make("admin");
1665 let user_addr = api.addr_make("foobar");
1666
1667 let mut wasm_keeper = wasm_keeper();
1668 let block = mock_env().block;
1669 let code_id = wasm_keeper.store_code(creator_addr, payout::contract());
1670
1671 let mut wasm_storage = MockStorage::new();
1672
1673 let contract_addr = wasm_keeper
1674 .register_contract(
1675 &api,
1676 &mut wasm_storage,
1677 code_id,
1678 user_addr.clone(),
1679 admin_addr,
1680 "label".to_owned(),
1681 1000,
1682 None,
1683 )
1684 .unwrap();
1685
1686 let payout = coin(1500, "mlg");
1688 let msg = payout::InstantiateMessage {
1689 payout: payout.clone(),
1690 };
1691 wasm_keeper
1692 .call_instantiate(
1693 contract_addr.clone(),
1694 &api,
1695 &mut wasm_storage,
1696 &mock_router(),
1697 &block,
1698 message_info(&user_addr, &[]),
1699 to_json_vec(&msg).unwrap(),
1700 )
1701 .unwrap();
1702
1703 let state = wasm_keeper.dump_wasm_raw(&wasm_storage, &contract_addr);
1705 assert_eq!(state.len(), 2);
1706 let (k, v) = &state[0];
1708 assert_eq!(k.as_slice(), b"count");
1709 let count: u32 = from_json(v).unwrap();
1710 assert_eq!(count, 1);
1711 let (k, v) = &state[1];
1712 assert_eq!(k.as_slice(), b"payout");
1713 let stored_pay: payout::InstantiateMessage = from_json(v).unwrap();
1714 assert_eq!(stored_pay.payout, payout);
1715 }
1716
1717 #[test]
1718 fn contract_send_coins() {
1719 let api = MockApi::default();
1720
1721 let creator_addr = api.addr_make("creator");
1723 let user_addr = api.addr_make("foobar");
1724
1725 let mut wasm_keeper = wasm_keeper();
1726 let block = mock_env().block;
1727 let code_id = wasm_keeper.store_code(creator_addr, payout::contract());
1728
1729 let mut wasm_storage = MockStorage::new();
1730 let mut cache = StorageTransaction::new(&wasm_storage);
1731
1732 let contract_addr = wasm_keeper
1733 .register_contract(
1734 &api,
1735 &mut cache,
1736 code_id,
1737 user_addr.clone(),
1738 None,
1739 "label".to_owned(),
1740 1000,
1741 None,
1742 )
1743 .unwrap();
1744
1745 let payout = coin(100, "TGD");
1746
1747 let info = message_info(&user_addr, &[]);
1749 let init_msg = to_json_vec(&payout::InstantiateMessage {
1750 payout: payout.clone(),
1751 })
1752 .unwrap();
1753 let res = wasm_keeper
1754 .call_instantiate(
1755 contract_addr.clone(),
1756 &api,
1757 &mut cache,
1758 &mock_router(),
1759 &block,
1760 info,
1761 init_msg,
1762 )
1763 .unwrap();
1764 assert_eq!(0, res.messages.len());
1765
1766 let info = message_info(&user_addr, &[]);
1768 let res = wasm_keeper
1769 .call_execute(
1770 &api,
1771 &mut cache,
1772 contract_addr.clone(),
1773 &mock_router(),
1774 &block,
1775 info,
1776 b"{}".to_vec(),
1777 )
1778 .unwrap();
1779 assert_eq!(1, res.messages.len());
1780 match &res.messages[0].msg {
1781 CosmosMsg::Bank(BankMsg::Send { to_address, amount }) => {
1782 assert_eq!(to_address.as_str(), user_addr.as_str());
1783 assert_eq!(amount.as_slice(), &[payout.clone()]);
1784 }
1785 m => panic!("Unexpected message {m:?}"),
1786 }
1787
1788 cache.prepare().commit(&mut wasm_storage);
1790
1791 let query = to_json_vec(&payout::QueryMsg::Payout {}).unwrap();
1793 let querier: MockQuerier<Empty> = MockQuerier::new(&[]);
1794 let data = wasm_keeper
1795 .query_smart(contract_addr, &api, &wasm_storage, &querier, &block, query)
1796 .unwrap();
1797 let res: payout::InstantiateMessage = from_json(data).unwrap();
1798 assert_eq!(res.payout, payout);
1799 }
1800
1801 fn assert_payout(
1802 router: &WasmKeeper<Empty, Empty>,
1803 storage: &mut dyn Storage,
1804 contract_addr: &Addr,
1805 payout: &Coin,
1806 ) {
1807 let api = MockApi::default();
1808 let user_addr = api.addr_make("silly");
1809 let info = message_info(&user_addr, &[]);
1810 let res = router
1811 .call_execute(
1812 &api,
1813 storage,
1814 contract_addr.clone(),
1815 &mock_router(),
1816 &mock_env().block,
1817 info,
1818 b"{}".to_vec(),
1819 )
1820 .unwrap();
1821 assert_eq!(1, res.messages.len());
1822 match &res.messages[0].msg {
1823 CosmosMsg::Bank(BankMsg::Send { to_address, amount }) => {
1824 assert_eq!(to_address.as_str(), user_addr.as_str());
1825 assert_eq!(amount.as_slice(), &[payout.clone()]);
1826 }
1827 m => panic!("Unexpected message {m:?}"),
1828 }
1829 }
1830
1831 fn assert_no_contract(storage: &dyn Storage, contract_addr: &Addr) {
1832 let contract = CONTRACTS.may_load(storage, contract_addr).unwrap();
1833 assert!(contract.is_none(), "{contract_addr:?}");
1834 }
1835
1836 #[test]
1837 fn multi_level_wasm_cache() {
1838 let api = MockApi::default();
1839
1840 let creator_addr = api.addr_make("creator");
1842 let user_addr = api.addr_make("foobar");
1843 let user_addr_1 = api.addr_make("johnny");
1844
1845 let mut wasm_keeper = wasm_keeper();
1846 let block = mock_env().block;
1847 let code_id = wasm_keeper.store_code(creator_addr, payout::contract());
1848
1849 let mut wasm_storage = MockStorage::new();
1850
1851 let payout1 = coin(100, "TGD");
1852
1853 let contract1 = transactional(&mut wasm_storage, |cache, _| {
1855 let contract = wasm_keeper
1856 .register_contract(
1857 &api,
1858 cache,
1859 code_id,
1860 user_addr.clone(),
1861 None,
1862 "".to_string(),
1863 1000,
1864 None,
1865 )
1866 .unwrap();
1867 let info = message_info(&user_addr, &[]);
1868 let init_msg = to_json_vec(&payout::InstantiateMessage {
1869 payout: payout1.clone(),
1870 })
1871 .unwrap();
1872 wasm_keeper
1873 .call_instantiate(
1874 contract.clone(),
1875 &api,
1876 cache,
1877 &mock_router(),
1878 &block,
1879 info,
1880 init_msg,
1881 )
1882 .unwrap();
1883
1884 Ok(contract)
1885 })
1886 .unwrap();
1887
1888 let payout2 = coin(50, "BTC");
1889 let payout3 = coin(1234, "ATOM");
1890
1891 let (contract2, contract3) = transactional(&mut wasm_storage, |cache, wasm_reader| {
1893 assert_payout(&wasm_keeper, cache, &contract1, &payout1);
1894
1895 let contract2 = wasm_keeper
1897 .register_contract(
1898 &api,
1899 cache,
1900 code_id,
1901 user_addr.clone(),
1902 None,
1903 "".to_owned(),
1904 1000,
1905 None,
1906 )
1907 .unwrap();
1908 let info = message_info(&user_addr, &[]);
1909 let init_msg = to_json_vec(&payout::InstantiateMessage {
1910 payout: payout2.clone(),
1911 })
1912 .unwrap();
1913 let _res = wasm_keeper
1914 .call_instantiate(
1915 contract2.clone(),
1916 &api,
1917 cache,
1918 &mock_router(),
1919 &block,
1920 info,
1921 init_msg,
1922 )
1923 .unwrap();
1924 assert_payout(&wasm_keeper, cache, &contract2, &payout2);
1925
1926 let contract3 = transactional(cache, |cache2, read| {
1928 assert_payout(&wasm_keeper, cache2, &contract1, &payout1);
1929 assert_payout(&wasm_keeper, cache2, &contract2, &payout2);
1930
1931 let contract3 = wasm_keeper
1933 .register_contract(
1934 &api,
1935 cache2,
1936 code_id,
1937 user_addr,
1938 None,
1939 "".to_owned(),
1940 1000,
1941 None,
1942 )
1943 .unwrap();
1944 let info = message_info(&user_addr_1, &[]);
1945 let init_msg = to_json_vec(&payout::InstantiateMessage {
1946 payout: payout3.clone(),
1947 })
1948 .unwrap();
1949 let _res = wasm_keeper
1950 .call_instantiate(
1951 contract3.clone(),
1952 &api,
1953 cache2,
1954 &mock_router(),
1955 &block,
1956 info,
1957 init_msg,
1958 )
1959 .unwrap();
1960 assert_payout(&wasm_keeper, cache2, &contract3, &payout3);
1961
1962 assert_no_contract(read, &contract3);
1964 Ok(contract3)
1965 })
1966 .unwrap();
1967
1968 assert_payout(&wasm_keeper, cache, &contract1, &payout1);
1970 assert_payout(&wasm_keeper, cache, &contract2, &payout2);
1971 assert_payout(&wasm_keeper, cache, &contract3, &payout3);
1972
1973 assert_no_contract(wasm_reader, &contract1);
1975 assert_no_contract(wasm_reader, &contract2);
1976 assert_no_contract(wasm_reader, &contract3);
1977
1978 Ok((contract2, contract3))
1979 })
1980 .unwrap();
1981
1982 assert_payout(&wasm_keeper, &mut wasm_storage, &contract1, &payout1);
1984 assert_payout(&wasm_keeper, &mut wasm_storage, &contract2, &payout2);
1985 assert_payout(&wasm_keeper, &mut wasm_storage, &contract3, &payout3);
1986 }
1987
1988 fn assert_admin(
1989 storage: &dyn Storage,
1990 wasm_keeper: &WasmKeeper<Empty, Empty>,
1991 contract_addr: &impl ToString,
1992 admin: Option<Addr>,
1993 ) {
1994 let api = MockApi::default();
1995 let querier: MockQuerier<Empty> = MockQuerier::new(&[]);
1996 let data = wasm_keeper
1998 .query(
1999 &api,
2000 storage,
2001 &querier,
2002 &mock_env().block,
2003 WasmQuery::ContractInfo {
2004 contract_addr: contract_addr.to_string(),
2005 },
2006 )
2007 .unwrap();
2008 let res: ContractInfoResponse = from_json(data).unwrap();
2009 assert_eq!(res.admin, admin);
2010 }
2011
2012 #[test]
2013 fn update_clear_admin_works() {
2014 let api = MockApi::default();
2015 let mut wasm_keeper = wasm_keeper();
2016 let block = mock_env().block;
2017 let creator = api.addr_make("creator");
2018 let code_id = wasm_keeper.store_code(creator.clone(), caller::contract());
2019
2020 let mut wasm_storage = MockStorage::new();
2021
2022 let admin = api.addr_make("admin");
2023 let new_admin = api.addr_make("new_admin");
2024 let normal_user = api.addr_make("normal_user");
2025
2026 let contract_addr = wasm_keeper
2027 .register_contract(
2028 &api,
2029 &mut wasm_storage,
2030 code_id,
2031 creator,
2032 admin.clone(),
2033 "label".to_owned(),
2034 1000,
2035 None,
2036 )
2037 .unwrap();
2038
2039 let info = message_info(&admin, &[]);
2041 let init_msg = to_json_vec(&Empty {}).unwrap();
2042 let res = wasm_keeper
2043 .call_instantiate(
2044 contract_addr.clone(),
2045 &api,
2046 &mut wasm_storage,
2047 &mock_router(),
2048 &block,
2049 info,
2050 init_msg,
2051 )
2052 .unwrap();
2053 assert_eq!(0, res.messages.len());
2054
2055 assert_admin(
2056 &wasm_storage,
2057 &wasm_keeper,
2058 &contract_addr,
2059 Some(admin.clone()),
2060 );
2061
2062 wasm_keeper
2064 .execute_wasm(
2065 &api,
2066 &mut wasm_storage,
2067 &mock_router(),
2068 &block,
2069 normal_user.clone(),
2070 WasmMsg::UpdateAdmin {
2071 contract_addr: contract_addr.to_string(),
2072 admin: normal_user.to_string(),
2073 },
2074 )
2075 .unwrap_err();
2076
2077 assert_admin(
2079 &wasm_storage,
2080 &wasm_keeper,
2081 &contract_addr,
2082 Some(admin.clone()),
2083 );
2084
2085 let res = wasm_keeper
2087 .execute_wasm(
2088 &api,
2089 &mut wasm_storage,
2090 &mock_router(),
2091 &block,
2092 admin,
2093 WasmMsg::UpdateAdmin {
2094 contract_addr: contract_addr.to_string(),
2095 admin: new_admin.to_string(),
2096 },
2097 )
2098 .unwrap();
2099 assert_eq!(res.events.len(), 0);
2100
2101 assert_admin(
2103 &wasm_storage,
2104 &wasm_keeper,
2105 &contract_addr,
2106 Some(new_admin.clone()),
2107 );
2108
2109 let res = wasm_keeper
2111 .execute_wasm(
2112 &api,
2113 &mut wasm_storage,
2114 &mock_router(),
2115 &block,
2116 new_admin,
2117 WasmMsg::ClearAdmin {
2118 contract_addr: contract_addr.to_string(),
2119 },
2120 )
2121 .unwrap();
2122 assert_eq!(res.events.len(), 0);
2123
2124 assert_admin(&wasm_storage, &wasm_keeper, &contract_addr, None);
2126 }
2127
2128 #[test]
2129 fn uses_simple_address_generator_by_default() {
2130 let api = MockApi::default();
2131 let mut wasm_keeper = wasm_keeper();
2132 let creator_addr = api.addr_make("creator");
2133 let code_id = wasm_keeper.store_code(creator_addr.clone(), payout::contract());
2134 assert_eq!(1, code_id);
2135
2136 let mut wasm_storage = MockStorage::new();
2137
2138 let admin = api.addr_make("admin");
2139 let contract_addr = wasm_keeper
2140 .register_contract(
2141 &api,
2142 &mut wasm_storage,
2143 code_id,
2144 creator_addr.clone(),
2145 admin.clone(),
2146 "label".to_owned(),
2147 1000,
2148 None,
2149 )
2150 .unwrap();
2151
2152 assert_eq!(
2153 contract_addr.as_str(),
2154 "cosmwasm1mzdhwvvh22wrt07w59wxyd58822qavwkx5lcej7aqfkpqqlhaqfsgn6fq2",
2155 "default address generator returned incorrect address"
2156 );
2157
2158 let salt = HexBinary::from_hex("c0ffee").unwrap();
2159
2160 let contract_addr = wasm_keeper
2161 .register_contract(
2162 &api,
2163 &mut wasm_storage,
2164 code_id,
2165 creator_addr.clone(),
2166 admin.clone(),
2167 "label".to_owned(),
2168 1000,
2169 Binary::from(salt.clone()),
2170 )
2171 .unwrap();
2172
2173 assert_eq!(
2174 contract_addr.as_str(),
2175 "cosmwasm1drhu6t78wacgm5qjzs4hvkv9fd9awa9henw7fh6vmzrhf7k2nkjsg3flns",
2176 "default address generator returned incorrect address"
2177 );
2178
2179 let code_id = wasm_keeper.store_code(creator_addr, payout::contract());
2180 assert_eq!(2, code_id);
2181
2182 let user_addr = api.addr_make("boobaz");
2183
2184 let contract_addr = wasm_keeper
2185 .register_contract(
2186 &api,
2187 &mut wasm_storage,
2188 code_id,
2189 user_addr,
2190 admin,
2191 "label".to_owned(),
2192 1000,
2193 Binary::from(salt),
2194 )
2195 .unwrap();
2196
2197 assert_eq!(
2198 contract_addr.as_str(),
2199 "cosmwasm13cfeertf2gny0rzp5jwqzst8crmfgvcd2lq5su0c9z66yxa45qdsdd0uxc",
2200 "default address generator returned incorrect address"
2201 );
2202 }
2203
2204 struct TestAddressGenerator {
2205 address: Addr,
2206 predictable_address: Addr,
2207 }
2208
2209 impl AddressGenerator for TestAddressGenerator {
2210 fn contract_address(
2211 &self,
2212 _api: &dyn Api,
2213 _storage: &mut dyn Storage,
2214 _code_id: u64,
2215 _instance_id: u64,
2216 ) -> AnyResult<Addr> {
2217 Ok(self.address.clone())
2218 }
2219
2220 fn predictable_contract_address(
2221 &self,
2222 _api: &dyn Api,
2223 _storage: &mut dyn Storage,
2224 _code_id: u64,
2225 _instance_id: u64,
2226 _checksum: &[u8],
2227 _creator: &CanonicalAddr,
2228 _salt: &[u8],
2229 ) -> AnyResult<Addr> {
2230 Ok(self.predictable_address.clone())
2231 }
2232 }
2233
2234 #[test]
2235 fn can_use_custom_address_generator() {
2236 let api = MockApi::default();
2237 let expected_addr = api.addr_make("address");
2238 let expected_predictable_addr = api.addr_make("predictable_address");
2239 let mut wasm_keeper: WasmKeeper<Empty, Empty> =
2240 WasmKeeper::new().with_address_generator(TestAddressGenerator {
2241 address: expected_addr.clone(),
2242 predictable_address: expected_predictable_addr.clone(),
2243 });
2244 let creator = api.addr_make("creator");
2245 let code_id = wasm_keeper.store_code(creator.clone(), payout::contract());
2246
2247 let mut wasm_storage = MockStorage::new();
2248
2249 let admin = api.addr_make("admin");
2250 let contract_addr = wasm_keeper
2251 .register_contract(
2252 &api,
2253 &mut wasm_storage,
2254 code_id,
2255 creator.clone(),
2256 admin.clone(),
2257 "label".to_owned(),
2258 1000,
2259 None,
2260 )
2261 .unwrap();
2262
2263 assert_eq!(
2264 contract_addr, expected_addr,
2265 "custom address generator returned incorrect address"
2266 );
2267
2268 let contract_addr = wasm_keeper
2269 .register_contract(
2270 &api,
2271 &mut wasm_storage,
2272 code_id,
2273 creator,
2274 admin,
2275 "label".to_owned(),
2276 1000,
2277 Binary::from(HexBinary::from_hex("23A74B8C").unwrap()),
2278 )
2279 .unwrap();
2280
2281 assert_eq!(
2282 contract_addr, expected_predictable_addr,
2283 "custom address generator returned incorrect address"
2284 );
2285 }
2286}