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