abstract_core/objects/
module.rs

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