1use std::{
2 fmt::{self, Display},
3 str::FromStr,
4};
5
6use cosmwasm_std::{ensure_eq, to_json_binary, Addr, Binary, QuerierWrapper, StdError, StdResult};
7use cw2::ContractVersion;
8use cw_storage_plus::{Key, KeyDeserialize, Prefixer, PrimaryKey};
9use semver::Version;
10
11use super::module_reference::ModuleReference;
12use crate::{
13 error::AbstractError,
14 objects::{fee::FixedFee, module_version::MODULE, namespace::Namespace},
15 AbstractResult, IBC_CLIENT,
16};
17
18pub type ModuleId<'a> = &'a str;
20
21#[cosmwasm_schema::cw_serde]
23pub enum ModuleStatus {
24 Registered,
26 Pending,
28 Yanked,
30}
31
32#[cosmwasm_schema::cw_serde]
34pub struct ModuleInfo {
35 pub namespace: Namespace,
37 pub name: String,
39 pub version: ModuleVersion,
41}
42
43impl TryFrom<ModuleInfo> for ContractVersion {
44 type Error = AbstractError;
45
46 fn try_from(value: ModuleInfo) -> Result<Self, Self::Error> {
47 let ModuleVersion::Version(version) = value.version else {
48 return Err(AbstractError::MissingVersion("module".to_owned()));
49 };
50 Ok(ContractVersion {
51 contract: format!("{}:{}", value.namespace, value.name),
52 version,
53 })
54 }
55}
56
57const MAX_LENGTH: usize = 64;
58
59pub fn validate_name(name: &str) -> AbstractResult<()> {
63 if name.is_empty() {
64 return Err(AbstractError::FormattingError {
65 object: "module name".into(),
66 expected: "with content".into(),
67 actual: "empty".to_string(),
68 });
69 }
70 if name.len() > MAX_LENGTH {
71 return Err(AbstractError::FormattingError {
72 object: "module name".into(),
73 expected: "at most 64 characters".into(),
74 actual: name.len().to_string(),
75 });
76 }
77 if name.contains(|c: char| !c.is_ascii_alphanumeric() && c != '-') {
78 return Err(AbstractError::FormattingError {
79 object: "module name".into(),
80 expected: "alphanumeric characters and hyphens".into(),
81 actual: name.to_string(),
82 });
83 }
84
85 if name != name.to_lowercase() {
86 return Err(AbstractError::FormattingError {
87 object: "module name".into(),
88 expected: name.to_ascii_lowercase(),
89 actual: name.to_string(),
90 });
91 }
92 Ok(())
93}
94
95impl ModuleInfo {
96 pub fn from_id(id: &str, version: ModuleVersion) -> AbstractResult<Self> {
97 let split: Vec<&str> = id.split(':').collect();
98 if split.len() != 2 {
99 return Err(AbstractError::FormattingError {
100 object: "contract id".into(),
101 expected: "namespace:contract_name".to_string(),
102 actual: id.to_string(),
103 });
104 }
105 Ok(ModuleInfo {
106 namespace: Namespace::try_from(split[0])?,
107 name: split[1].to_lowercase(),
108 version,
109 })
110 }
111 pub fn from_id_latest(id: &str) -> AbstractResult<Self> {
112 Self::from_id(id, ModuleVersion::Latest)
113 }
114
115 pub fn validate(&self) -> AbstractResult<()> {
116 self.namespace.validate()?;
117 validate_name(&self.name)?;
118 self.version.validate().map_err(|e| {
119 StdError::generic_err(format!("Invalid version for module {}: {}", self.id(), e))
120 })?;
121 Ok(())
122 }
123
124 pub fn id(&self) -> String {
125 format!("{}:{}", self.namespace, self.name)
126 }
127
128 pub fn id_with_version(&self) -> String {
129 format!("{}:{}", self.id(), self.version)
130 }
131
132 pub fn assert_version_variant(&self) -> AbstractResult<()> {
133 match &self.version {
134 ModuleVersion::Latest => Err(AbstractError::Assert(
135 "Module version must be set to a specific version".into(),
136 )),
137 ModuleVersion::Version(ver) => {
138 semver::Version::parse(ver)?;
140 Ok(())
141 }
142 }
143 }
144}
145
146impl PrimaryKey<'_> for &ModuleInfo {
147 type Prefix = (Namespace, String);
149
150 type SubPrefix = Namespace;
152
153 type Suffix = ModuleVersion;
155
156 type SuperSuffix = (String, ModuleVersion);
158
159 fn key(&self) -> Vec<cw_storage_plus::Key> {
160 let mut keys = self.namespace.key();
161 keys.extend(self.name.key());
162 keys.extend(self.version.key());
163 keys
164 }
165}
166
167impl Prefixer<'_> for &ModuleInfo {
168 fn prefix(&self) -> Vec<Key> {
169 let mut res = self.namespace.prefix();
170 res.extend(self.name.prefix());
171 res.extend(self.version.prefix());
172 res
173 }
174}
175
176impl KeyDeserialize for &ModuleInfo {
177 type Output = ModuleInfo;
178 const KEY_ELEMS: u16 = Namespace::KEY_ELEMS + String::KEY_ELEMS + ModuleVersion::KEY_ELEMS;
179
180 #[inline(always)]
181 fn from_vec(mut value: Vec<u8>) -> StdResult<Self::Output> {
182 let mut prov_name_ver = value.split_off(2);
183 let prov_len = parse_length(&value)?;
184 let mut len_name_ver = prov_name_ver.split_off(prov_len);
185
186 let mut name_ver = len_name_ver.split_off(2);
187 let ver_len = parse_length(&len_name_ver)?;
188 let ver = name_ver.split_off(ver_len);
189
190 Ok(ModuleInfo {
191 namespace: Namespace::try_from(String::from_vec(prov_name_ver)?).map_err(|e| {
192 StdError::generic_err(format!("Invalid namespace for module: {}", e))
193 })?,
194 name: String::from_vec(name_ver)?,
195 version: ModuleVersion::from_vec(ver)?,
196 })
197 }
198}
199
200impl KeyDeserialize for ModuleVersion {
201 type Output = ModuleVersion;
202 const KEY_ELEMS: u16 = 1;
203
204 #[inline(always)]
205 fn from_vec(value: Vec<u8>) -> StdResult<Self::Output> {
206 let val = String::from_vec(value)?;
207 if &val == "latest" {
208 Ok(Self::Latest)
209 } else {
210 Ok(Self::Version(val))
211 }
212 }
213}
214
215#[inline(always)]
216fn parse_length(value: &[u8]) -> StdResult<usize> {
217 Ok(u16::from_be_bytes(
218 value
219 .try_into()
220 .map_err(|_| StdError::generic_err("Could not read 2 byte length"))?,
221 )
222 .into())
223}
224
225#[cosmwasm_schema::cw_serde]
226pub enum ModuleVersion {
227 Latest,
228 Version(String),
229}
230
231impl ModuleVersion {
232 pub fn validate(&self) -> AbstractResult<()> {
233 match &self {
234 ModuleVersion::Latest => Ok(()),
235 ModuleVersion::Version(ver) => {
236 Version::parse(ver)?;
238 Ok(())
239 }
240 }
241 }
242}
243
244impl FromStr for ModuleVersion {
245 type Err = AbstractError;
246
247 fn from_str(s: &str) -> Result<Self, Self::Err> {
248 match s {
249 "latest" => Ok(Self::Latest),
250 _ => {
251 let v = Self::Version(s.to_owned());
252 v.validate()?;
253 Ok(v)
254 }
255 }
256 }
257}
258
259impl Display for ModuleVersion {
261 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
262 let print_str = match self {
263 ModuleVersion::Latest => "latest".to_string(),
264 ModuleVersion::Version(ver) => ver.to_owned(),
265 };
266 f.write_str(&print_str)
267 }
268}
269
270impl<T> From<T> for ModuleVersion
271where
272 T: Into<String>,
273{
274 fn from(ver: T) -> Self {
275 Self::Version(ver.into())
276 }
277}
278
279impl fmt::Display for ModuleInfo {
280 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281 write!(
282 f,
283 "{} provided by {} with version {}",
284 self.name, self.namespace, self.version,
285 )
286 }
287}
288
289impl TryInto<Version> for ModuleVersion {
290 type Error = AbstractError;
291
292 fn try_into(self) -> AbstractResult<Version> {
293 match self {
294 ModuleVersion::Latest => Err(AbstractError::MissingVersion("module".to_string())),
295 ModuleVersion::Version(ver) => {
296 let version = Version::parse(&ver)?;
297 Ok(version)
298 }
299 }
300 }
301}
302
303impl PrimaryKey<'_> for ModuleVersion {
304 type Prefix = ();
305
306 type SubPrefix = ();
307
308 type Suffix = Self;
309
310 type SuperSuffix = Self;
311
312 fn key(&self) -> Vec<cw_storage_plus::Key> {
313 match &self {
314 ModuleVersion::Latest => "latest".key(),
315 ModuleVersion::Version(ver) => ver.key(),
316 }
317 }
318}
319
320impl Prefixer<'_> for ModuleVersion {
321 fn prefix(&self) -> Vec<Key> {
322 let self_as_bytes = match &self {
323 ModuleVersion::Latest => "latest".as_bytes(),
324 ModuleVersion::Version(ver) => ver.as_bytes(),
325 };
326 vec![Key::Ref(self_as_bytes)]
327 }
328}
329
330impl TryFrom<ContractVersion> for ModuleInfo {
331 type Error = AbstractError;
332
333 fn try_from(value: ContractVersion) -> Result<Self, Self::Error> {
334 let split: Vec<&str> = value.contract.split(':').collect();
335 if split.len() != 2 {
336 return Err(AbstractError::FormattingError {
337 object: "contract id".to_string(),
338 expected: "namespace:contract_name".into(),
339 actual: value.contract,
340 });
341 }
342 Ok(ModuleInfo {
343 namespace: Namespace::try_from(split[0])?,
344 name: split[1].to_lowercase(),
345 version: ModuleVersion::Version(value.version),
346 })
347 }
348}
349
350#[cosmwasm_schema::cw_serde]
351pub struct Module {
352 pub info: ModuleInfo,
353 pub reference: ModuleReference,
354}
355
356impl fmt::Display for Module {
357 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
358 write!(f, "info: {}, reference: {:?}", self.info, self.reference)
359 }
360}
361
362impl From<(ModuleInfo, ModuleReference)> for Module {
363 fn from((info, reference): (ModuleInfo, ModuleReference)) -> Self {
364 Self { info, reference }
365 }
366}
367
368impl Module {
369 pub fn should_be_whitelisted(&self) -> bool {
371 match &self.reference {
372 ModuleReference::Adapter(_) | ModuleReference::App(_) => true,
374 ModuleReference::Native(_) if self.info.id() == IBC_CLIENT => true,
375 _ => false,
376 }
377 }
378}
379
380#[cosmwasm_schema::cw_serde]
381pub struct ModuleInitMsg {
382 pub fixed_init: Option<Binary>,
383 pub owner_init: Option<Binary>,
384}
385
386impl ModuleInitMsg {
387 pub fn format(self) -> AbstractResult<Binary> {
388 match self {
389 ModuleInitMsg {
391 fixed_init: Some(_),
392 owner_init: Some(_),
393 } => to_json_binary(&self),
394 ModuleInitMsg {
396 fixed_init: None,
397 owner_init: Some(r),
398 } => Ok(r),
399 ModuleInitMsg {
400 fixed_init: Some(f),
401 owner_init: None,
402 } => Ok(f),
403 ModuleInitMsg {
404 fixed_init: None,
405 owner_init: None,
406 } => Err(StdError::generic_err("No init msg set for this module")),
407 }
408 .map_err(Into::into)
409 }
410}
411
412pub fn assert_module_data_validity(
414 querier: &QuerierWrapper,
415 module_claim: &Module,
417 module_address: Option<Addr>,
419) -> AbstractResult<()> {
420 let module_address = match &module_claim.reference.unwrap_addr() {
422 Ok(addr) => addr.to_owned(),
423 Err(..) => {
424 let Some(addr) = module_address else {
426 return Ok(());
429 };
430 addr
431 }
432 };
433
434 let ModuleVersion::Version(version) = &module_claim.info.version else {
435 panic!("Module version is not versioned, context setting is wrong")
436 };
437
438 let cw_2_data_res = cw2::CONTRACT.query(querier, module_address.clone());
440
441 if let ModuleReference::Standalone(_) | ModuleReference::Service(_) = module_claim.reference {
443 if let Ok(cw_2_data) = cw_2_data_res {
444 ensure_eq!(
445 version,
446 &cw_2_data.version,
447 AbstractError::UnequalModuleData {
448 cw2: cw_2_data.version,
449 module: version.to_owned()
450 }
451 );
452 }
453 return Ok(());
454 }
455 let cw_2_data = cw_2_data_res?;
456
457 ensure_eq!(
459 module_claim.info.id(),
460 cw_2_data.contract,
461 AbstractError::UnequalModuleData {
462 cw2: cw_2_data.contract,
463 module: module_claim.info.id()
464 }
465 );
466
467 ensure_eq!(
469 version,
470 &cw_2_data.version,
471 AbstractError::UnequalModuleData {
472 cw2: cw_2_data.version,
473 module: version.to_owned()
474 }
475 );
476 match module_claim.reference {
478 ModuleReference::Account(_) | ModuleReference::Native(_) | ModuleReference::Service(_) => {
479 return Ok(())
480 }
481 _ => {}
482 }
483
484 let module_data = MODULE.query(querier, module_address)?;
485 ensure_eq!(
487 module_data.module,
488 cw_2_data.contract,
489 AbstractError::UnequalModuleData {
490 cw2: cw_2_data.contract,
491 module: module_data.module,
492 }
493 );
494 ensure_eq!(
496 module_data.version,
497 cw_2_data.version,
498 AbstractError::UnequalModuleData {
499 cw2: cw_2_data.version,
500 module: module_data.version
501 }
502 );
503
504 Ok(())
505}
506
507#[cosmwasm_schema::cw_serde]
509#[non_exhaustive]
510pub enum Monetization {
511 None,
512 InstallFee(FixedFee),
513}
514
515impl Default for Monetization {
516 fn default() -> Self {
517 Self::None
518 }
519}
520
521pub type ModuleMetadata = String;
523
524#[cfg(test)]
529mod test {
530 #![allow(clippy::needless_borrows_for_generic_args)]
531 use cosmwasm_std::{testing::mock_dependencies, Addr, Order};
532 use cw_storage_plus::Map;
533
534 use super::*;
535
536 mod storage_plus {
537 use super::*;
538
539 fn mock_key() -> ModuleInfo {
540 ModuleInfo {
541 namespace: Namespace::new("abstract").unwrap(),
542 name: "rocket-ship".to_string(),
543 version: ModuleVersion::Version("1.9.9".into()),
544 }
545 }
546
547 fn mock_keys() -> (ModuleInfo, ModuleInfo, ModuleInfo, ModuleInfo) {
548 (
549 ModuleInfo {
550 namespace: Namespace::new("abstract").unwrap(),
551 name: "boat".to_string(),
552 version: ModuleVersion::Version("1.9.9".into()),
553 },
554 ModuleInfo {
555 namespace: Namespace::new("abstract").unwrap(),
556 name: "rocket-ship".to_string(),
557 version: ModuleVersion::Version("1.0.0".into()),
558 },
559 ModuleInfo {
560 namespace: Namespace::new("abstract").unwrap(),
561 name: "rocket-ship".to_string(),
562 version: ModuleVersion::Version("2.0.0".into()),
563 },
564 ModuleInfo {
565 namespace: Namespace::new("astroport").unwrap(),
566 name: "liquidity-pool".to_string(),
567 version: ModuleVersion::Version("10.5.7".into()),
568 },
569 )
570 }
571
572 #[coverage_helper::test]
573 fn storage_key_works() {
574 let mut deps = mock_dependencies();
575 let key = mock_key();
576 let map: Map<&ModuleInfo, u64> = Map::new("map");
577
578 map.save(deps.as_mut().storage, &key, &42069).unwrap();
579
580 assert_eq!(map.load(deps.as_ref().storage, &key).unwrap(), 42069);
581
582 let items = map
583 .range(deps.as_ref().storage, None, None, Order::Ascending)
584 .map(|item| item.unwrap())
585 .collect::<Vec<_>>();
586
587 assert_eq!(items.len(), 1);
588 assert_eq!(items[0], (key, 42069));
589 }
590
591 #[coverage_helper::test]
592 fn storage_key_with_overlapping_name_namespace() {
593 let mut deps = mock_dependencies();
594 let info1 = ModuleInfo {
595 namespace: Namespace::new("abstract").unwrap(),
596 name: "ans".to_string(),
597 version: ModuleVersion::Version("1.9.9".into()),
598 };
599
600 let _key1 = (&info1).joined_key();
601
602 let info2 = ModuleInfo {
603 namespace: Namespace::new("abs").unwrap(),
604 name: "tractans".to_string(),
605 version: ModuleVersion::Version("1.9.9".into()),
606 };
607
608 let _key2 = (&info2).joined_key();
609
610 let map: Map<&ModuleInfo, u64> = Map::new("map");
611
612 map.save(deps.as_mut().storage, &info1, &42069).unwrap();
613 map.save(deps.as_mut().storage, &info2, &69420).unwrap();
614
615 assert_eq!(
616 map.keys_raw(&deps.storage, None, None, Order::Ascending)
617 .collect::<Vec<_>>()
618 .len(),
619 2
620 );
621 }
622
623 #[coverage_helper::test]
624 fn composite_key_works() {
625 let mut deps = mock_dependencies();
626 let key = mock_key();
627 let map: Map<(&ModuleInfo, Addr), u64> = Map::new("map");
628
629 map.save(
630 deps.as_mut().storage,
631 (&key, Addr::unchecked("larry")),
632 &42069,
633 )
634 .unwrap();
635
636 map.save(
637 deps.as_mut().storage,
638 (&key, Addr::unchecked("jake")),
639 &69420,
640 )
641 .unwrap();
642
643 let items = map
644 .prefix(&key)
645 .range(deps.as_ref().storage, None, None, Order::Ascending)
646 .map(|item| item.unwrap())
647 .collect::<Vec<_>>();
648
649 assert_eq!(items.len(), 2);
650 assert_eq!(items[0], (Addr::unchecked("jake"), 69420));
651 assert_eq!(items[1], (Addr::unchecked("larry"), 42069));
652 }
653
654 #[coverage_helper::test]
655 fn partial_key_works() {
656 let mut deps = mock_dependencies();
657 let (key1, key2, key3, key4) = mock_keys();
658 let map: Map<&ModuleInfo, u64> = Map::new("map");
659
660 map.save(deps.as_mut().storage, &key1, &42069).unwrap();
661
662 map.save(deps.as_mut().storage, &key2, &69420).unwrap();
663
664 map.save(deps.as_mut().storage, &key3, &999).unwrap();
665
666 map.save(deps.as_mut().storage, &key4, &13).unwrap();
667
668 let items = map
669 .sub_prefix(Namespace::new("abstract").unwrap())
670 .range(deps.as_ref().storage, None, None, Order::Ascending)
671 .map(|item| item.unwrap())
672 .collect::<Vec<_>>();
673
674 assert_eq!(items.len(), 3);
675 assert_eq!(
676 items[0],
677 (
678 (
679 "boat".to_string(),
680 ModuleVersion::Version("1.9.9".to_string())
681 ),
682 42069
683 )
684 );
685 assert_eq!(
686 items[1],
687 (
688 (
689 "rocket-ship".to_string(),
690 ModuleVersion::Version("1.0.0".to_string())
691 ),
692 69420
693 )
694 );
695
696 assert_eq!(
697 items[2],
698 (
699 (
700 "rocket-ship".to_string(),
701 ModuleVersion::Version("2.0.0".to_string())
702 ),
703 999
704 )
705 );
706
707 let items = map
708 .sub_prefix(Namespace::new("astroport").unwrap())
709 .range(deps.as_ref().storage, None, None, Order::Ascending)
710 .map(|item| item.unwrap())
711 .collect::<Vec<_>>();
712
713 assert_eq!(items.len(), 1);
714 assert_eq!(
715 items[0],
716 (
717 (
718 "liquidity-pool".to_string(),
719 ModuleVersion::Version("10.5.7".to_string())
720 ),
721 13
722 )
723 );
724 }
725
726 #[coverage_helper::test]
727 fn partial_key_versions_works() {
728 let mut deps = mock_dependencies();
729 let (key1, key2, key3, key4) = mock_keys();
730 let map: Map<&ModuleInfo, u64> = Map::new("map");
731
732 map.save(deps.as_mut().storage, &key1, &42069).unwrap();
733
734 map.save(deps.as_mut().storage, &key2, &69420).unwrap();
735
736 map.save(deps.as_mut().storage, &key3, &999).unwrap();
737
738 map.save(deps.as_mut().storage, &key4, &13).unwrap();
739
740 let items = map
741 .prefix((
742 Namespace::new("abstract").unwrap(),
743 "rocket-ship".to_string(),
744 ))
745 .range(deps.as_ref().storage, None, None, Order::Ascending)
746 .map(|item| item.unwrap())
747 .collect::<Vec<_>>();
748
749 assert_eq!(items.len(), 2);
750 assert_eq!(
751 items[0],
752 (ModuleVersion::Version("1.0.0".to_string()), 69420)
753 );
754
755 assert_eq!(items[1], (ModuleVersion::Version("2.0.0".to_string()), 999));
756 }
757 }
758
759 mod module_info {
760 use super::*;
761
762 #[coverage_helper::test]
763 fn validate_with_empty_name() {
764 let info = ModuleInfo {
765 namespace: Namespace::try_from("abstract").unwrap(),
766 name: "".to_string(),
767 version: ModuleVersion::Version("1.9.9".into()),
768 };
769
770 assert!(info.validate().unwrap_err().to_string().contains("empty"));
771 }
772
773 #[coverage_helper::test]
774 fn validate_with_empty_namespace() {
775 let info = ModuleInfo {
776 namespace: Namespace::unchecked(""),
777 name: "ans".to_string(),
778 version: ModuleVersion::Version("1.9.9".into()),
779 };
780
781 assert!(info.validate().unwrap_err().to_string().contains("empty"));
782 }
783
784 use rstest::rstest;
785
786 #[rstest]
787 #[case("ans_host")]
788 #[case("ans:host")]
789 #[case("ans-host&")]
790 fn validate_fails_with_non_alphanumeric(#[case] name: &str) {
791 let info = ModuleInfo {
792 namespace: Namespace::try_from("abstract").unwrap(),
793 name: name.to_string(),
794 version: ModuleVersion::Version("1.9.9".into()),
795 };
796
797 assert!(info
798 .validate()
799 .unwrap_err()
800 .to_string()
801 .contains("alphanumeric"));
802 }
803
804 #[rstest]
805 #[case("lmao")]
806 #[case("bad-")]
807 fn validate_with_bad_versions(#[case] version: &str) {
808 let info = ModuleInfo {
809 namespace: Namespace::try_from("abstract").unwrap(),
810 name: "ans".to_string(),
811 version: ModuleVersion::Version(version.into()),
812 };
813
814 assert!(info
815 .validate()
816 .unwrap_err()
817 .to_string()
818 .contains("Invalid version"));
819 }
820
821 #[coverage_helper::test]
822 fn id() {
823 let info = ModuleInfo {
824 name: "name".to_string(),
825 namespace: Namespace::try_from("namespace").unwrap(),
826 version: ModuleVersion::Version("1.0.0".into()),
827 };
828
829 let expected = "namespace:name".to_string();
830
831 assert_eq!(info.id(), expected);
832 }
833
834 #[coverage_helper::test]
835 fn id_with_version() {
836 let info = ModuleInfo {
837 name: "name".to_string(),
838 namespace: Namespace::try_from("namespace").unwrap(),
839 version: ModuleVersion::Version("1.0.0".into()),
840 };
841
842 let expected = "namespace:name:1.0.0".to_string();
843
844 assert_eq!(info.id_with_version(), expected);
845 }
846 }
847
848 mod module_version {
849 use super::*;
850
851 #[coverage_helper::test]
852 fn try_into_version_happy_path() {
853 let version = ModuleVersion::Version("1.0.0".into());
854
855 let expected: Version = "1.0.0".to_string().parse().unwrap();
856
857 let actual: Version = version.try_into().unwrap();
858
859 assert_eq!(actual, expected);
860 }
861
862 #[coverage_helper::test]
863 fn try_into_version_with_latest() {
864 let version = ModuleVersion::Latest;
865
866 let actual: Result<Version, _> = version.try_into();
867
868 assert!(actual.is_err());
869 }
870 }
871
872 mod standalone_modules_valid {
873 use cosmwasm_std::testing::MOCK_CONTRACT_ADDR;
874
875 use super::*;
876
877 #[coverage_helper::test]
878 fn no_cw2_contract() {
879 let deps = mock_dependencies();
880 let res = assert_module_data_validity(
881 &deps.as_ref().querier,
882 &Module {
883 info: ModuleInfo {
884 namespace: Namespace::new("counter").unwrap(),
885 name: "counter".to_owned(),
886 version: ModuleVersion::Version("1.1.0".to_owned()),
887 },
888 reference: ModuleReference::Standalone(0),
889 },
890 Some(Addr::unchecked(MOCK_CONTRACT_ADDR)),
891 );
892 assert!(res.is_ok());
893 }
894 }
895}