abstract_ans_host/
commands.rs

1use abstract_sdk::execute_update_ownership;
2use abstract_std::{
3    ans_host::{state::*, AssetPair, ExecuteMsg},
4    objects::{
5        pool_id::{PoolAddress, UncheckedPoolAddress},
6        pool_metadata::PoolMetadata,
7        pool_reference::PoolReference,
8        AssetEntry, DexAssetPairing, DexName, UncheckedChannelEntry, UncheckedContractEntry,
9        UniquePoolId,
10    },
11};
12use cosmwasm_std::{Addr, DepsMut, Env, MessageInfo, StdError, StdResult, Storage};
13use cw_asset::AssetInfoUnchecked;
14
15use crate::{
16    contract::{AnsHostResponse, AnsHostResult},
17    error::{AnsHostError, AnsHostError::InvalidAssetCount},
18};
19
20const MIN_POOL_ASSETS: usize = 2;
21const MAX_POOL_ASSETS: usize = 5;
22
23/// Handles the common base execute messages
24pub fn handle_message(
25    deps: DepsMut,
26    info: MessageInfo,
27    env: Env,
28    message: ExecuteMsg,
29) -> AnsHostResult {
30    match message {
31        ExecuteMsg::UpdateContractAddresses { to_add, to_remove } => {
32            update_contract_addresses(deps, info, to_add, to_remove)
33        }
34        ExecuteMsg::UpdateAssetAddresses { to_add, to_remove } => {
35            update_asset_addresses(deps, info, to_add, to_remove)
36        }
37        ExecuteMsg::UpdateChannels { to_add, to_remove } => {
38            update_channels(deps, info, to_add, to_remove)
39        }
40        ExecuteMsg::UpdateDexes { to_add, to_remove } => {
41            update_dex_registry(deps, info, to_add, to_remove)
42        }
43        ExecuteMsg::UpdatePools { to_add, to_remove } => {
44            update_pools(deps, info, to_add, to_remove)
45        }
46        ExecuteMsg::UpdateOwnership(action) => {
47            execute_update_ownership!(AnsHostResponse, deps, env, info, action)
48        }
49    }
50}
51
52//----------------------------------------------------------------------------------------
53//  GOVERNANCE CONTROLLED SETTERS
54//----------------------------------------------------------------------------------------
55
56/// Adds, updates or removes provided addresses.
57pub fn update_contract_addresses(
58    deps: DepsMut,
59    msg_info: MessageInfo,
60    to_add: Vec<(UncheckedContractEntry, String)>,
61    to_remove: Vec<UncheckedContractEntry>,
62) -> AnsHostResult {
63    // Only Admin can call this method
64    cw_ownable::assert_owner(deps.storage, &msg_info.sender)?;
65
66    for (key, new_address) in to_add.into_iter() {
67        let key = key.check();
68        // validate addr
69        let addr = deps.as_ref().api.addr_validate(&new_address)?;
70
71        // Update function for new or existing keys
72        let insert = |_| -> StdResult<Addr> { Ok(addr) };
73        CONTRACT_ADDRESSES.update(deps.storage, &key, insert)?;
74    }
75
76    for key in to_remove {
77        let key = key.check();
78        CONTRACT_ADDRESSES.remove(deps.storage, &key);
79    }
80
81    Ok(AnsHostResponse::action("update_contract_addresses"))
82}
83
84/// Adds, updates or removes provided addresses.
85pub fn update_asset_addresses(
86    deps: DepsMut,
87    msg_info: MessageInfo,
88    to_add: Vec<(String, AssetInfoUnchecked)>,
89    to_remove: Vec<String>,
90) -> AnsHostResult {
91    // Only Admin can call this method
92    cw_ownable::assert_owner(deps.storage, &msg_info.sender)?;
93
94    for (name, new_asset) in to_add.into_iter() {
95        // validate asset
96        let asset = new_asset.check(deps.as_ref().api, None)?;
97
98        let entry = AssetEntry::from(name);
99
100        ASSET_ADDRESSES.save(deps.storage, &entry, &asset)?;
101        REV_ASSET_ADDRESSES.save(deps.storage, &asset, &entry)?;
102    }
103
104    for name in to_remove {
105        let entry = AssetEntry::from(name);
106        let maybe_asset = ASSET_ADDRESSES.may_load(deps.storage, &entry)?;
107        if let Some(asset) = maybe_asset {
108            ASSET_ADDRESSES.remove(deps.storage, &entry);
109            REV_ASSET_ADDRESSES.remove(deps.storage, &asset);
110        }
111    }
112
113    Ok(AnsHostResponse::action("update_asset_addresses"))
114}
115
116/// Adds, updates or removes provided addresses.
117pub fn update_channels(
118    deps: DepsMut,
119    msg_info: MessageInfo,
120    to_add: Vec<(UncheckedChannelEntry, String)>,
121    to_remove: Vec<UncheckedChannelEntry>,
122) -> AnsHostResult {
123    // Only Admin can call this method
124    cw_ownable::assert_owner(deps.storage, &msg_info.sender)?;
125
126    for (key, new_channel) in to_add.into_iter() {
127        let key = key.check()?;
128        // Update function for new or existing keys
129        let insert = |_| -> StdResult<String> { Ok(new_channel) };
130        CHANNELS.update(deps.storage, &key, insert)?;
131    }
132
133    for key in to_remove {
134        let key = key.check()?;
135        CHANNELS.remove(deps.storage, &key);
136    }
137
138    Ok(AnsHostResponse::action("update_channels"))
139}
140
141/// Updates the dex registry with additions and removals
142fn update_dex_registry(
143    deps: DepsMut,
144    msg_info: MessageInfo,
145    to_add: Vec<String>,
146    to_remove: Vec<String>,
147) -> AnsHostResult {
148    // Only Admin can call this method
149    cw_ownable::assert_owner(deps.storage, &msg_info.sender)?;
150
151    if !to_add.is_empty() {
152        let register_dex = |mut dexes: Vec<String>| -> StdResult<Vec<String>> {
153            for dex in to_add {
154                if !dexes.contains(&dex) {
155                    dexes.push(dex.to_ascii_lowercase());
156                }
157            }
158            Ok(dexes)
159        };
160
161        REGISTERED_DEXES.update(deps.storage, register_dex)?;
162    }
163
164    if !to_remove.is_empty() {
165        let deregister_dex = |mut dexes: Vec<String>| -> StdResult<Vec<String>> {
166            for dex in to_remove {
167                dexes.retain(|x| x != &dex);
168            }
169            Ok(dexes)
170        };
171        REGISTERED_DEXES.update(deps.storage, deregister_dex)?;
172    }
173
174    Ok(AnsHostResponse::action("update_dexes"))
175}
176
177fn update_pools(
178    deps: DepsMut,
179    msg_info: MessageInfo,
180    to_add: Vec<(UncheckedPoolAddress, PoolMetadata)>,
181    to_remove: Vec<UniquePoolId>,
182) -> AnsHostResult {
183    // Only Admin can call this method
184    cw_ownable::assert_owner(deps.storage, &msg_info.sender)?;
185
186    let original_unique_pool_id = CONFIG.load(deps.storage)?.next_unique_pool_id;
187    let mut next_unique_pool_id = original_unique_pool_id;
188
189    // only load dexes if necessary
190    let registered_dexes = if to_add.is_empty() {
191        vec![]
192    } else {
193        REGISTERED_DEXES.load(deps.storage)?
194    };
195
196    for (pool_id, mut pool_metadata) in to_add.into_iter() {
197        let pool_id = pool_id.check(deps.api)?;
198
199        let assets = &mut pool_metadata.assets;
200        validate_pool_assets(deps.storage, assets)?;
201
202        let dex = pool_metadata.dex.to_ascii_lowercase();
203        if !registered_dexes.contains(&dex) {
204            return Err(AnsHostError::UnregisteredDex { dex });
205        }
206
207        // Register each pair of assets as a pairing and link it to the pool id
208        register_pool_pairings(deps.storage, next_unique_pool_id, pool_id, assets, &dex)?;
209
210        POOL_METADATA.save(deps.storage, next_unique_pool_id, &pool_metadata)?;
211
212        // Increment the unique pool id for the next pool
213        next_unique_pool_id.increment();
214    }
215
216    for pool_id_to_remove in to_remove {
217        // load the pool metadata
218        let pool_metadata = POOL_METADATA.may_load(deps.storage, pool_id_to_remove)?;
219
220        let pool_metadata = match pool_metadata {
221            Some(pool_metadata) => pool_metadata,
222            // THere is no existing metadata at that id, so we can skip it
223            None => continue,
224        };
225
226        remove_pool_pairings(
227            deps.storage,
228            pool_id_to_remove,
229            &pool_metadata.dex,
230            &pool_metadata.assets,
231        )?;
232
233        // remove the pool metadata
234        POOL_METADATA.remove(deps.storage, pool_id_to_remove);
235    }
236
237    // Only update the next pool id if necessary
238    if next_unique_pool_id != original_unique_pool_id {
239        CONFIG.update(deps.storage, |mut config| -> StdResult<_> {
240            config.next_unique_pool_id = next_unique_pool_id;
241            Ok(config)
242        })?;
243    }
244
245    Ok(AnsHostResponse::action("update_pools"))
246}
247
248/// Execute an action on every asset pairing in the list of assets
249/// Example: assets: [A, B, C] -> [A, B], [A, C], [B, C]
250fn exec_on_asset_pairings<T, A, E>(assets: &[AssetEntry], mut action: A) -> StdResult<()>
251where
252    A: FnMut(AssetPair) -> Result<T, E>,
253    StdError: From<E>,
254{
255    for (i, asset_x) in assets.iter().enumerate() {
256        for (j, asset_y) in assets.iter().enumerate() {
257            // Skip self-pairings
258            if i == j || asset_x == asset_y {
259                continue;
260            }
261            let pair: AssetPair = (asset_x.clone(), asset_y.clone());
262            action(pair)?;
263        }
264    }
265    Ok(())
266}
267
268fn register_pool_pairings(
269    storage: &mut dyn Storage,
270    next_pool_id: UniquePoolId,
271    pool_address: PoolAddress,
272    assets: &[AssetEntry],
273    dex: &DexName,
274) -> StdResult<()> {
275    let register_pairing = |(asset_x, asset_y): AssetPair| {
276        let key = DexAssetPairing::new(asset_x, asset_y, dex);
277
278        let compound_pool_id = PoolReference {
279            unique_id: next_pool_id,
280            pool_address: pool_address.clone(),
281        };
282
283        register_asset_pairing(storage, key, compound_pool_id)
284    };
285
286    exec_on_asset_pairings(assets, register_pairing)
287}
288
289/// Register an asset pairing to its pool id
290/// We ignore any duplicates, which is why we don't check for them
291fn register_asset_pairing(
292    storage: &mut dyn Storage,
293    pair: DexAssetPairing,
294    compound_pool_id: PoolReference,
295) -> Result<Vec<PoolReference>, StdError> {
296    let insert = |ids: Option<Vec<PoolReference>>| -> StdResult<_> {
297        let mut ids = ids.unwrap_or_default();
298
299        ids.push(compound_pool_id);
300        Ok(ids)
301    };
302
303    ASSET_PAIRINGS.update(storage, &pair, insert)
304}
305
306/// Remove the unique_pool_id (which is getting removed) from the list of pool ids for each asset pairing
307fn remove_pool_pairings(
308    storage: &mut dyn Storage,
309    pool_id_to_remove: UniquePoolId,
310    dex: &DexName,
311    assets: &[AssetEntry],
312) -> StdResult<()> {
313    let remove_pairing_action = |(asset_x, asset_y): AssetPair| -> Result<(), StdError> {
314        let key = DexAssetPairing::new(asset_x, asset_y, dex);
315
316        // Action to remove the pool id from the list of pool ids for the asset pairing
317        let remove_pool_id_action = |ids: Option<Vec<PoolReference>>| -> StdResult<_> {
318            let mut ids = ids.unwrap_or_default();
319            ids.retain(|id| id.unique_id != pool_id_to_remove);
320            Ok(ids)
321        };
322
323        let remaining_ids = ASSET_PAIRINGS.update(storage, &key, remove_pool_id_action)?;
324
325        // If there are no remaining pools, remove the asset pair from the map
326        if remaining_ids.is_empty() {
327            ASSET_PAIRINGS.remove(storage, &key);
328        }
329        Ok(())
330    };
331
332    exec_on_asset_pairings(assets, remove_pairing_action)
333}
334
335/// unsure
336fn validate_pool_assets(
337    storage: &dyn Storage,
338    assets: &mut [AssetEntry],
339) -> Result<(), AnsHostError> {
340    // convert all assets to lower
341    for asset in assets.iter_mut() {
342        asset.format();
343    }
344
345    if assets.len() < MIN_POOL_ASSETS || assets.len() > MAX_POOL_ASSETS {
346        return Err(InvalidAssetCount {
347            min: MIN_POOL_ASSETS,
348            max: MAX_POOL_ASSETS,
349            provided: assets.len(),
350        });
351    }
352
353    // Validate that each exists in the asset registry
354    for asset in assets.iter() {
355        if ASSET_ADDRESSES.may_load(storage, asset)?.is_none() {
356            return Err(AnsHostError::UnregisteredAsset {
357                asset: asset.to_string(),
358            });
359        }
360    }
361    Ok(())
362}
363
364#[cfg(test)]
365mod test {
366    #![allow(clippy::needless_borrows_for_generic_args)]
367    use abstract_testing::{map_tester::CwMapTester, prelude::*};
368    use cosmwasm_std::{testing::*, Addr};
369
370    use super::*;
371    use crate::{contract, error::AnsHostError, test_common::*};
372
373    type AnsHostTestResult = Result<(), AnsHostError>;
374
375    fn execute_helper(deps: &mut MockDeps, msg: ExecuteMsg, owner: &Addr) -> AnsHostTestResult {
376        let env = mock_env_validated(deps.api);
377        contract::execute(deps.as_mut(), env, message_info(owner, &[]), msg)?;
378        Ok(())
379    }
380
381    fn register_assets_helper(
382        deps: &mut MockDeps,
383        assets: Vec<AssetEntry>,
384        owner: &Addr,
385    ) -> AnsHostTestResult {
386        let msg = ExecuteMsg::UpdateAssetAddresses {
387            to_add: assets
388                .iter()
389                .map(|a| (a.to_string(), AssetInfoUnchecked::native(a.to_string())))
390                .collect(),
391            to_remove: vec![],
392        };
393        execute_helper(deps, msg, owner)?;
394        Ok(())
395    }
396
397    mod update_dexes {
398        use super::*;
399
400        use abstract_testing::mock_env_validated;
401        use cosmwasm_std::{testing::MockApi, Empty, OwnedDeps};
402
403        #[coverage_helper::test]
404        fn register_dex() -> AnsHostTestResult {
405            let mut deps = mock_dependencies();
406            let abstr = AbstractMockAddrs::new(deps.api);
407            mock_init(&mut deps).unwrap();
408
409            let info = message_info(&abstr.owner, &[]);
410            let new_dex = "test_dex".to_string();
411
412            let msg = ExecuteMsg::UpdateDexes {
413                to_add: vec![new_dex.clone()],
414                to_remove: vec![],
415            };
416            let env = mock_env_validated(deps.api);
417
418            let _res = contract::execute(deps.as_mut(), env, info, msg)?;
419
420            assert_expected_dexes(&deps, vec![new_dex]);
421
422            Ok(())
423        }
424
425        /// Registering multiple dexes should work
426        #[coverage_helper::test]
427        fn register_dex_twice() -> AnsHostTestResult {
428            let mut deps = mock_dependencies();
429            let abstr = AbstractMockAddrs::new(deps.api);
430            let env = mock_env_validated(deps.api);
431            mock_init(&mut deps).unwrap();
432
433            let info = message_info(&abstr.owner, &[]);
434            let new_dex = "test_dex".to_string();
435
436            let msg = ExecuteMsg::UpdateDexes {
437                to_add: vec![new_dex.clone()],
438                to_remove: vec![],
439            };
440
441            let _res = contract::execute(deps.as_mut(), env.clone(), info.clone(), msg.clone())?;
442            let _res = contract::execute(deps.as_mut(), env, info, msg)?;
443
444            assert_expected_dexes(&deps, vec![new_dex]);
445
446            Ok(())
447        }
448
449        #[coverage_helper::test]
450        fn duplicate_in_msg() -> AnsHostTestResult {
451            let mut deps = mock_dependencies();
452            let abstr = AbstractMockAddrs::new(deps.api);
453            let env = mock_env_validated(deps.api);
454            mock_init(&mut deps).unwrap();
455
456            let info = message_info(&abstr.owner, &[]);
457            let new_dex = "test_dex".to_string();
458
459            let msg = ExecuteMsg::UpdateDexes {
460                to_add: vec![new_dex.clone(), new_dex.clone()],
461                to_remove: vec![],
462            };
463
464            let _res = contract::execute(deps.as_mut(), env, info, msg)?;
465
466            // ONly one dex should be registered
467            assert_expected_dexes(&deps, vec![new_dex]);
468
469            Ok(())
470        }
471
472        #[coverage_helper::test]
473        fn register_and_deregister_dex_same_msg() -> AnsHostTestResult {
474            let mut deps = mock_dependencies();
475            let abstr = AbstractMockAddrs::new(deps.api);
476            let env = mock_env_validated(deps.api);
477            mock_init(&mut deps).unwrap();
478
479            let info = message_info(&abstr.owner, &[]);
480            let new_dex = "test_dex".to_string();
481
482            let msg = ExecuteMsg::UpdateDexes {
483                to_add: vec![new_dex.clone()],
484                to_remove: vec![new_dex],
485            };
486
487            let _res = contract::execute(deps.as_mut(), env, info, msg)?;
488
489            assert_expected_dexes(&deps, vec![]);
490
491            Ok(())
492        }
493
494        #[coverage_helper::test]
495        fn register_multiple_dexes() -> AnsHostTestResult {
496            let mut deps = mock_dependencies();
497            let abstr = AbstractMockAddrs::new(deps.api);
498            let env = mock_env_validated(deps.api);
499            mock_init(&mut deps).unwrap();
500
501            let info = message_info(&abstr.owner, &[]);
502            let new_dexes = vec!["test_dex".to_string(), "test_dex_2".to_string()];
503
504            let msg = ExecuteMsg::UpdateDexes {
505                to_add: new_dexes.clone(),
506                to_remove: vec![],
507            };
508
509            let _res = contract::execute(deps.as_mut(), env, info, msg)?;
510
511            assert_expected_dexes(&deps, new_dexes);
512
513            Ok(())
514        }
515
516        #[coverage_helper::test]
517        fn remove_nonexistent_dex() -> AnsHostTestResult {
518            let mut deps = mock_dependencies();
519            let abstr = AbstractMockAddrs::new(deps.api);
520            let env = mock_env_validated(deps.api);
521            mock_init(&mut deps).unwrap();
522
523            let info = message_info(&abstr.owner, &[]);
524            let missing_dex = "test_dex".to_string();
525
526            let msg = ExecuteMsg::UpdateDexes {
527                to_add: vec![],
528                to_remove: vec![missing_dex],
529            };
530
531            let _res = contract::execute(deps.as_mut(), env, info, msg)?;
532
533            let expected_dexes: Vec<String> = vec![];
534
535            assert_expected_dexes(&deps, expected_dexes);
536
537            Ok(())
538        }
539
540        fn assert_expected_dexes(
541            deps: &OwnedDeps<MockStorage, MockApi, MockQuerier, Empty>,
542            expected_dexes: Vec<String>,
543        ) {
544            let actual_dexes = REGISTERED_DEXES.load(&deps.storage).unwrap();
545
546            assert_eq!(actual_dexes, expected_dexes);
547        }
548    }
549
550    mod update_contract_addresses {
551        use abstract_std::{ans_host::ContractMapEntry, objects::ContractEntry};
552        use abstract_testing::map_tester::CwMapTesterBuilder;
553
554        use super::*;
555
556        fn contract_entry(namespace: &str, name: &str) -> UncheckedContractEntry {
557            UncheckedContractEntry {
558                protocol: namespace.to_string(),
559                contract: name.to_string(),
560            }
561        }
562
563        fn contract_address_map_entry(
564            namespace: &str,
565            name: &str,
566            address: &Addr,
567        ) -> (UncheckedContractEntry, String) {
568            (contract_entry(namespace, name), address.to_string())
569        }
570
571        fn mock_contract_map_entry(address: &Addr) -> (UncheckedContractEntry, String) {
572            contract_address_map_entry("test_namespace", "test_contract", address)
573        }
574
575        fn update_contract_addresses_msg_builder(
576            to_add: Vec<(UncheckedContractEntry, String)>,
577            to_remove: Vec<UncheckedContractEntry>,
578        ) -> ExecuteMsg {
579            ExecuteMsg::UpdateContractAddresses { to_add, to_remove }
580        }
581
582        fn from_checked_entry((key, value): ContractMapEntry) -> (UncheckedContractEntry, String) {
583            (
584                UncheckedContractEntry {
585                    protocol: key.protocol,
586                    contract: key.contract,
587                },
588                value.into(),
589            )
590        }
591
592        fn setup_map_tester<'a>(
593            mock_api: MockApi,
594        ) -> CwMapTester<
595            ExecuteMsg,
596            AnsHostError,
597            &'a ContractEntry,
598            Addr,
599            UncheckedContractEntry,
600            String,
601        > {
602            let abstr = AbstractMockAddrs::new(mock_api);
603            let info = message_info(&abstr.owner, &[]);
604            let test_addr = mock_api.addr_make("test_address");
605
606            CwMapTesterBuilder::default()
607                .info(info)
608                .map(CONTRACT_ADDRESSES)
609                .execute(contract::execute)
610                .msg_builder(update_contract_addresses_msg_builder)
611                .mock_entry(mock_contract_map_entry(&test_addr))
612                .from_checked_entry(from_checked_entry)
613                .build()
614                .unwrap()
615        }
616
617        #[coverage_helper::test]
618        fn add_contract_address() -> AnsHostTestResult {
619            let mut deps = mock_dependencies();
620            mock_init(&mut deps).unwrap();
621
622            let mut map_tester = setup_map_tester(deps.api);
623            map_tester.test_add_one(&mut deps)
624        }
625
626        #[coverage_helper::test]
627        fn add_contract_address_twice() -> AnsHostTestResult {
628            let mut deps = mock_dependencies();
629            mock_init(&mut deps).unwrap();
630
631            let mut map_tester = setup_map_tester(deps.api);
632            map_tester.test_add_one_twice(&mut deps)
633        }
634
635        #[coverage_helper::test]
636        fn add_contract_address_twice_in_same_msg() -> AnsHostTestResult {
637            let mut deps = mock_dependencies();
638            mock_init(&mut deps).unwrap();
639
640            let mut map_tester = setup_map_tester(deps.api);
641            map_tester.test_add_two_same(&mut deps)
642        }
643
644        #[coverage_helper::test]
645        fn add_and_remove_contract_address_same_msg() -> AnsHostTestResult {
646            let mut deps = mock_dependencies();
647            mock_init(&mut deps).unwrap();
648
649            let mut map_tester = setup_map_tester(deps.api);
650            map_tester.test_add_and_remove_same(&mut deps)
651        }
652
653        #[coverage_helper::test]
654        fn remove_non_existent_contract_address() -> AnsHostTestResult {
655            let mut deps = mock_dependencies();
656            mock_init(&mut deps).unwrap();
657
658            let mut map_tester = setup_map_tester(deps.api);
659            map_tester.test_remove_nonexistent(&mut deps)
660        }
661
662        #[coverage_helper::test]
663        fn add_multiple_contract_addresses() -> AnsHostTestResult {
664            let mut deps = mock_dependencies();
665            mock_init(&mut deps).unwrap();
666            let mut map_tester = setup_map_tester(deps.api);
667
668            let new_entry_1 = contract_address_map_entry(
669                "test_namespace",
670                "test_contract",
671                &deps.api.addr_make("test_address"),
672            );
673            let new_entry_2 = contract_address_map_entry(
674                "test_namespace_2",
675                "test_contract_2",
676                &deps.api.addr_make("test_address_2"),
677            );
678            let new_entry_3 = contract_address_map_entry(
679                "test_namespace_3",
680                "test_contract_3",
681                &deps.api.addr_make("test_address_3"),
682            );
683
684            map_tester.test_update_auto_expect(
685                &mut deps,
686                (vec![new_entry_1, new_entry_2, new_entry_3], vec![]),
687            )
688        }
689
690        #[coverage_helper::test]
691        fn add_multiple_contract_addresses_and_deregister_one() -> AnsHostTestResult {
692            let mut deps = mock_dependencies();
693            mock_init(&mut deps).unwrap();
694            let mut map_tester = setup_map_tester(deps.api);
695
696            let new_entry_1 = contract_address_map_entry(
697                "test_namespace",
698                "test_contract",
699                &deps.api.addr_make("test_address"),
700            );
701            let new_entry_2 = contract_address_map_entry(
702                "test_namespace_2",
703                "test_contract_2",
704                &deps.api.addr_make("test_address_2"),
705            );
706
707            // add 1 and 2
708            map_tester.test_update_auto_expect(
709                &mut deps,
710                (vec![new_entry_1.clone(), new_entry_2.clone()], vec![]),
711            )?;
712
713            let new_entry_3 = contract_address_map_entry(
714                "test_namespace_3",
715                "test_contract_3",
716                &deps.api.addr_make("test_address_3"),
717            );
718
719            // Add 3 and remove 1, leaving 2 and 3
720            map_tester.test_update_with_expected(
721                &mut deps,
722                (vec![new_entry_3.clone()], vec![new_entry_1.0]),
723                vec![new_entry_2, new_entry_3],
724            )
725        }
726    }
727
728    mod update_asset_addresses {
729        use super::*;
730
731        use abstract_testing::map_tester::CwMapTesterBuilder;
732        use cw_asset::{AssetInfo, AssetInfoBase};
733        use cw_storage_plus::Map;
734
735        fn unchecked_asset_map_entry(
736            name: &str,
737            info: AssetInfoUnchecked,
738        ) -> (String, AssetInfoUnchecked) {
739            (name.into(), info)
740        }
741
742        fn mock_asset_map_entry() -> (String, AssetInfoUnchecked) {
743            let name = "test";
744            let info = AssetInfoUnchecked::native("utest".to_string());
745
746            unchecked_asset_map_entry(name, info)
747        }
748
749        fn update_asset_addresses_msg_builder(
750            to_add: Vec<(String, AssetInfoUnchecked)>,
751            to_remove: Vec<String>,
752        ) -> ExecuteMsg {
753            ExecuteMsg::UpdateAssetAddresses { to_add, to_remove }
754        }
755
756        fn from_checked_entry(
757            (key, value): (AssetEntry, AssetInfo),
758        ) -> (String, AssetInfoUnchecked) {
759            (key.to_string(), value.into())
760        }
761
762        fn mock_unchecked_entries(
763            mock_api: MockApi,
764        ) -> (
765            (String, AssetInfoUnchecked),
766            (String, AssetInfoUnchecked),
767            (String, AssetInfoUnchecked),
768        ) {
769            let new_entry_1 =
770                unchecked_asset_map_entry("juno", AssetInfoBase::Native("ujuno".into()));
771            let new_entry_2 =
772                unchecked_asset_map_entry("osmo", AssetInfoBase::Native("uosmo".into()));
773            let new_entry_3 = unchecked_asset_map_entry(
774                "sjuno",
775                AssetInfoBase::Cw20(mock_api.addr_make("sjuno").to_string()),
776            );
777            (new_entry_1, new_entry_2, new_entry_3)
778        }
779
780        fn setup_map_tester<'a>(
781            mock_api: MockApi,
782        ) -> CwMapTester<
783            ExecuteMsg,
784            AnsHostError,
785            &'a AssetEntry,
786            AssetInfo,
787            String,
788            AssetInfoUnchecked,
789        > {
790            let abstr = AbstractMockAddrs::new(mock_api);
791            let info = message_info(&abstr.owner, &[]);
792
793            CwMapTesterBuilder::default()
794                .info(info)
795                .map(ASSET_ADDRESSES)
796                .execute(contract::execute)
797                .msg_builder(update_asset_addresses_msg_builder)
798                .mock_entry(mock_asset_map_entry())
799                .from_checked_entry(from_checked_entry)
800                .build()
801                .unwrap()
802        }
803
804        #[coverage_helper::test]
805        fn add_asset_address() -> AnsHostTestResult {
806            let mut deps = mock_dependencies();
807            mock_init(&mut deps).unwrap();
808
809            let mut map_tester = setup_map_tester(deps.api);
810            map_tester.test_add_one(&mut deps)?;
811            let reverse_map = REV_ASSET_ADDRESSES;
812            let test_entry =
813                reverse_map.load(&deps.storage, &AssetInfoBase::Native("utest".into()))?;
814            assert_eq!(test_entry, AssetEntry::from("test"));
815            Ok(())
816        }
817
818        #[coverage_helper::test]
819        fn add_asset_address_twice() -> AnsHostTestResult {
820            let mut deps = mock_dependencies();
821            mock_init(&mut deps).unwrap();
822
823            let mut map_tester = setup_map_tester(deps.api);
824            map_tester.test_add_one_twice(&mut deps)
825        }
826
827        #[coverage_helper::test]
828        fn add_asset_address_twice_in_same_msg() -> AnsHostTestResult {
829            let mut deps = mock_dependencies();
830            mock_init(&mut deps).unwrap();
831
832            let mut map_tester = setup_map_tester(deps.api);
833            map_tester.test_add_two_same(&mut deps)
834        }
835
836        #[coverage_helper::test]
837        fn add_and_remove_asset_address_same_msg() -> AnsHostTestResult {
838            let mut deps = mock_dependencies();
839            mock_init(&mut deps).unwrap();
840
841            let mut map_tester = setup_map_tester(deps.api);
842            map_tester.test_add_and_remove_same(&mut deps)?;
843            let reverse_map = Map::<&AssetInfo, AssetEntry>::new("rev_assets");
844            let test_entry =
845                reverse_map.may_load(&deps.storage, &AssetInfoBase::Native("utest".into()))?;
846            assert_eq!(test_entry, None);
847            Ok(())
848        }
849
850        #[coverage_helper::test]
851        fn remove_non_existent_asset_address() -> AnsHostTestResult {
852            let mut deps = mock_dependencies();
853            mock_init(&mut deps).unwrap();
854
855            let mut map_tester = setup_map_tester(deps.api);
856            map_tester.test_remove_nonexistent(&mut deps)
857        }
858
859        #[coverage_helper::test]
860        fn add_multiple_asset_addresses() -> AnsHostTestResult {
861            let mut deps = mock_dependencies();
862            mock_init(&mut deps).unwrap();
863            let mut map_tester = setup_map_tester(deps.api);
864
865            let (new_entry_1, new_entry_2, new_entry_3) = mock_unchecked_entries(deps.api);
866            map_tester.test_update_auto_expect(
867                &mut deps,
868                (vec![new_entry_1, new_entry_2, new_entry_3.clone()], vec![]),
869            )?;
870
871            let reverse_map = REV_ASSET_ADDRESSES;
872            let test_entry =
873                reverse_map.load(&deps.storage, &new_entry_3.1.check(&deps.api, None)?)?;
874            assert_eq!(test_entry.to_string(), new_entry_3.0);
875            Ok(())
876        }
877
878        #[coverage_helper::test]
879        fn add_multiple_asset_addresses_and_deregister_one() -> AnsHostTestResult {
880            let mut deps = mock_dependencies();
881            mock_init(&mut deps).unwrap();
882            let mut map_tester = setup_map_tester(deps.api);
883
884            let (new_entry_1, new_entry_2, _new_entry_3) = mock_unchecked_entries(deps.api);
885
886            // add 1 and 2
887            map_tester.test_update_auto_expect(
888                &mut deps,
889                (vec![new_entry_1.clone(), new_entry_2.clone()], vec![]),
890            )?;
891
892            let new_entry_3 = unchecked_asset_map_entry(
893                "usd",
894                AssetInfoBase::Cw20(deps.api.addr_make("uusd").into()),
895            );
896
897            // Add 3 and remove 1, leaving 2 and 3
898            map_tester.test_update_with_expected(
899                &mut deps,
900                (vec![new_entry_3.clone()], vec![new_entry_1.0]),
901                vec![new_entry_2, new_entry_3],
902            )
903        }
904    }
905
906    mod update_channels {
907        use abstract_std::objects::ChannelEntry;
908        use abstract_testing::map_tester::CwMapTesterBuilder;
909
910        use super::*;
911
912        type UncheckedChannelMapEntry = (UncheckedChannelEntry, String);
913
914        fn update_channels_msg_builder(
915            to_add: Vec<UncheckedChannelMapEntry>,
916            to_remove: Vec<UncheckedChannelEntry>,
917        ) -> ExecuteMsg {
918            ExecuteMsg::UpdateChannels { to_add, to_remove }
919        }
920
921        fn from_checked_entry((key, value): (ChannelEntry, String)) -> UncheckedChannelMapEntry {
922            (
923                UncheckedChannelEntry {
924                    connected_chain: key.clone().connected_chain.to_string(),
925                    protocol: key.protocol,
926                },
927                value,
928            )
929        }
930
931        fn unchecked_channel_map_entry(
932            chain: &str,
933            protocol: &str,
934            channel_id: &str,
935        ) -> UncheckedChannelMapEntry {
936            let channel_entry = UncheckedChannelEntry::new(chain, protocol);
937            (channel_entry, channel_id.to_string())
938        }
939
940        fn mock_unchecked_channel_map_entry() -> UncheckedChannelMapEntry {
941            unchecked_channel_map_entry("test-chain", "test_protocol", "test_channel_id")
942        }
943
944        fn mock_unchecked_channel_entries() -> (
945            UncheckedChannelMapEntry,
946            UncheckedChannelMapEntry,
947            UncheckedChannelMapEntry,
948        ) {
949            let new_entry_1 =
950                unchecked_channel_map_entry("test-chain", "test_contract_1", "test_channel_1");
951            let new_entry_2 =
952                unchecked_channel_map_entry("test-chain", "test_contract_2", "test_channel_2");
953            let new_entry_3 =
954                unchecked_channel_map_entry("test-chain", "test_contract_3", "test_channel_3");
955            (new_entry_1, new_entry_2, new_entry_3)
956        }
957
958        fn setup_map_tester<'a>(
959            mock_api: MockApi,
960        ) -> CwMapTester<
961            ExecuteMsg,
962            AnsHostError,
963            &'a ChannelEntry,
964            String,
965            UncheckedChannelEntry,
966            String,
967        > {
968            let abstr = AbstractMockAddrs::new(mock_api);
969            let info = message_info(&abstr.owner, &[]);
970
971            CwMapTesterBuilder::default()
972                .info(info)
973                .map(CHANNELS)
974                .execute(contract::execute)
975                .msg_builder(update_channels_msg_builder)
976                .mock_entry(mock_unchecked_channel_map_entry())
977                .from_checked_entry(from_checked_entry)
978                .build()
979                .unwrap()
980        }
981
982        #[coverage_helper::test]
983        fn add_channel() -> AnsHostTestResult {
984            let mut deps = mock_dependencies();
985            mock_init(&mut deps).unwrap();
986
987            let mut map_tester = setup_map_tester(deps.api);
988            map_tester.test_add_one(&mut deps)
989        }
990
991        #[coverage_helper::test]
992        fn add_channel_twice() -> AnsHostTestResult {
993            let mut deps = mock_dependencies();
994            mock_init(&mut deps).unwrap();
995
996            let mut map_tester = setup_map_tester(deps.api);
997            map_tester.test_add_one_twice(&mut deps)
998        }
999
1000        #[coverage_helper::test]
1001        fn add_channel_twice_in_same_msg() -> AnsHostTestResult {
1002            let mut deps = mock_dependencies();
1003            mock_init(&mut deps).unwrap();
1004
1005            let mut map_tester = setup_map_tester(deps.api);
1006            map_tester.test_add_two_same(&mut deps)
1007        }
1008
1009        #[coverage_helper::test]
1010        fn add_and_remove_channel_same_msg() -> AnsHostTestResult {
1011            let mut deps = mock_dependencies();
1012            mock_init(&mut deps).unwrap();
1013
1014            let mut map_tester = setup_map_tester(deps.api);
1015            map_tester.test_add_and_remove_same(&mut deps)
1016        }
1017
1018        #[coverage_helper::test]
1019        fn remove_non_existent_channel() -> AnsHostTestResult {
1020            let mut deps = mock_dependencies();
1021            mock_init(&mut deps).unwrap();
1022
1023            let mut map_tester = setup_map_tester(deps.api);
1024            map_tester.test_remove_nonexistent(&mut deps)
1025        }
1026
1027        #[coverage_helper::test]
1028        fn add_multiple_channels() -> AnsHostTestResult {
1029            let mut deps = mock_dependencies();
1030            mock_init(&mut deps).unwrap();
1031            let mut map_tester = setup_map_tester(deps.api);
1032
1033            let (new_entry_1, new_entry_2, new_entry_3) = mock_unchecked_channel_entries();
1034
1035            map_tester.test_update_auto_expect(
1036                &mut deps,
1037                (vec![new_entry_1, new_entry_2, new_entry_3], vec![]),
1038            )
1039        }
1040
1041        #[coverage_helper::test]
1042        fn add_multiple_channels_and_deregister_one() -> AnsHostTestResult {
1043            let mut deps = mock_dependencies();
1044            mock_init(&mut deps).unwrap();
1045            let mut map_tester = setup_map_tester(deps.api);
1046
1047            let (new_entry_1, new_entry_2, _new_entry_3) = mock_unchecked_channel_entries();
1048
1049            // add 1 and 2
1050            map_tester.test_update_auto_expect(
1051                &mut deps,
1052                (vec![new_entry_1.clone(), new_entry_2.clone()], vec![]),
1053            )?;
1054
1055            let new_entry_3 =
1056                unchecked_channel_map_entry("test-chain", "test_contract_3", "test_address_3");
1057
1058            // Add 3 and remove 1, leaving 2 and 3
1059            map_tester.test_update_with_expected(
1060                &mut deps,
1061                (vec![new_entry_3.clone()], vec![new_entry_1.0]),
1062                vec![new_entry_2, new_entry_3],
1063            )
1064        }
1065
1066        #[coverage_helper::test]
1067        fn upper_channel_entry_goes_lower() -> AnsHostTestResult {
1068            let mut deps = mock_dependencies();
1069            mock_init(&mut deps).unwrap();
1070            let mut map_tester = setup_map_tester(deps.api);
1071
1072            let upper_entry =
1073                unchecked_channel_map_entry("test-chain", "UP_PROTOCOL", "channel_id");
1074
1075            map_tester.execute_update(deps.as_mut(), (vec![upper_entry], vec![]))?;
1076
1077            let expected_entry =
1078                unchecked_channel_map_entry("test-chain", "up_protocol", "channel_id");
1079            map_tester.assert_expected_entries(&deps.storage, vec![expected_entry]);
1080
1081            Ok(())
1082        }
1083    }
1084
1085    mod update_pools {
1086        use super::*;
1087
1088        use abstract_std::{
1089            ans_host::{AssetPairingMapEntry, PoolMetadataMapEntry},
1090            objects::PoolType,
1091            AbstractResult,
1092        };
1093        use cosmwasm_std::{Api, Order};
1094
1095        type UncheckedPoolMapEntry = (UncheckedPoolAddress, PoolMetadata);
1096
1097        const INITIAL_UNIQUE_POOL_ID: u64 = 1;
1098
1099        // Makes a stable
1100        fn pool_metadata(dex: &str, pool_type: PoolType, assets: Vec<AssetEntry>) -> PoolMetadata {
1101            PoolMetadata {
1102                dex: dex.to_string(),
1103                pool_type,
1104                assets,
1105            }
1106        }
1107
1108        fn _mock_pool_metadata() -> PoolMetadata {
1109            pool_metadata(
1110                "junoswap",
1111                PoolType::Weighted,
1112                vec!["juno".into(), "osmo".into()],
1113            )
1114        }
1115
1116        fn unchecked_pool_map_entry(
1117            pool_contract_addr: &Addr,
1118            metadata: PoolMetadata,
1119        ) -> UncheckedPoolMapEntry {
1120            let pool_id = UncheckedPoolAddress::contract(pool_contract_addr);
1121            (pool_id, metadata)
1122        }
1123
1124        fn build_update_msg(
1125            to_add: Vec<UncheckedPoolMapEntry>,
1126            to_remove: Vec<UniquePoolId>,
1127        ) -> ExecuteMsg {
1128            ExecuteMsg::UpdatePools { to_add, to_remove }
1129        }
1130
1131        fn execute_update(
1132            deps: &mut MockDeps,
1133            (to_add, to_remove): (Vec<UncheckedPoolMapEntry>, Vec<UniquePoolId>),
1134            owner: &Addr,
1135        ) -> AnsHostTestResult {
1136            let msg = build_update_msg(to_add, to_remove);
1137            execute_helper(deps, msg, owner)?;
1138            Ok(())
1139        }
1140
1141        fn register_dex(deps: &mut MockDeps, dex: &str, owner: &Addr) -> AnsHostTestResult {
1142            let msg = ExecuteMsg::UpdateDexes {
1143                to_add: vec![dex.into()],
1144                to_remove: vec![],
1145            };
1146            execute_helper(deps, msg, owner)?;
1147            Ok(())
1148        }
1149
1150        fn load_pool_metadata(
1151            storage: &dyn Storage,
1152        ) -> Result<Vec<PoolMetadataMapEntry>, StdError> {
1153            POOL_METADATA
1154                .range(storage, None, None, Order::Ascending)
1155                .collect()
1156        }
1157
1158        fn load_asset_pairings(
1159            storage: &dyn Storage,
1160        ) -> Result<Vec<AssetPairingMapEntry>, StdError> {
1161            ASSET_PAIRINGS
1162                .range(storage, None, None, Order::Ascending)
1163                .collect()
1164        }
1165
1166        fn asset_pairing(
1167            api: &dyn Api,
1168            dex: &str,
1169            (asset_x, asset_y): (AssetEntry, AssetEntry),
1170            unchecked_pool_id: &UncheckedPoolAddress,
1171        ) -> AbstractResult<(DexAssetPairing, Vec<PoolReference>)> {
1172            Ok((
1173                DexAssetPairing::new(asset_x, asset_y, dex),
1174                vec![PoolReference::new(
1175                    INITIAL_UNIQUE_POOL_ID.into(),
1176                    unchecked_pool_id.clone().check(api)?,
1177                )],
1178            ))
1179        }
1180
1181        #[coverage_helper::test]
1182        fn add_pool() -> AnsHostTestResult {
1183            let mut deps = mock_dependencies();
1184            mock_init(&mut deps).unwrap();
1185            let abstr = AbstractMockAddrs::new(deps.api);
1186
1187            let dex = "junoswap";
1188
1189            let pool_assets = vec!["juno".into(), "osmo".into()];
1190            let metadata = pool_metadata(dex, PoolType::Weighted, pool_assets.clone());
1191
1192            // Register the assets in ANS
1193            register_assets_helper(&mut deps, pool_assets, &abstr.owner)?;
1194            register_dex(&mut deps, dex, &abstr.owner)?;
1195
1196            let new_entry = unchecked_pool_map_entry(&deps.api.addr_make("xxxx"), metadata.clone());
1197
1198            execute_update(&mut deps, (vec![new_entry.clone()], vec![]), &abstr.owner)?;
1199
1200            let expected_pools: Vec<PoolMetadataMapEntry> =
1201                vec![(INITIAL_UNIQUE_POOL_ID.into(), metadata)];
1202            let actual_pools: Result<Vec<PoolMetadataMapEntry>, _> =
1203                load_pool_metadata(&deps.storage);
1204
1205            assert_eq!(actual_pools?, expected_pools);
1206
1207            let _pairing =
1208                DexAssetPairing::<AssetEntry>::new("juno".into(), "osmo".into(), "junoswap");
1209
1210            let (unchecked_pool_id, _) = new_entry;
1211
1212            let expected_pairings = vec![
1213                asset_pairing(
1214                    &deps.api,
1215                    "junoswap",
1216                    ("juno".into(), "osmo".into()),
1217                    &unchecked_pool_id,
1218                )?,
1219                asset_pairing(
1220                    &deps.api,
1221                    "junoswap",
1222                    ("osmo".into(), "juno".into()),
1223                    &unchecked_pool_id,
1224                )?,
1225            ];
1226            let actual_pairings: Result<Vec<AssetPairingMapEntry>, _> =
1227                load_asset_pairings(&deps.storage);
1228            assert_eq!(actual_pairings?, expected_pairings);
1229
1230            Ok(())
1231        }
1232
1233        #[coverage_helper::test]
1234        fn add_five_asset_pool() -> AnsHostTestResult {
1235            let mut deps = mock_dependencies();
1236            mock_init(&mut deps).unwrap();
1237            let abstr = AbstractMockAddrs::new(deps.api);
1238
1239            let dex = "junoswap";
1240
1241            let pool_assets = vec![
1242                "juno".into(),
1243                "osmo".into(),
1244                "atom".into(),
1245                "uatom".into(),
1246                "uusd".into(),
1247            ];
1248            let metadata = pool_metadata(dex, PoolType::Weighted, pool_assets.clone());
1249
1250            // Register the assets in ANS
1251            register_assets_helper(&mut deps, pool_assets, &abstr.owner)?;
1252            register_dex(&mut deps, dex, &abstr.owner)?;
1253
1254            let new_entry = unchecked_pool_map_entry(&deps.api.addr_make("xxxx"), metadata.clone());
1255
1256            execute_update(&mut deps, (vec![new_entry.clone()], vec![]), &abstr.owner)?;
1257
1258            let expected_pools: Vec<PoolMetadataMapEntry> =
1259                vec![(INITIAL_UNIQUE_POOL_ID.into(), metadata)];
1260            let actual_pools: Result<Vec<PoolMetadataMapEntry>, _> =
1261                load_pool_metadata(&deps.storage);
1262
1263            assert_eq!(actual_pools?, expected_pools);
1264
1265            let _pairing =
1266                DexAssetPairing::<AssetEntry>::new("juno".into(), "osmo".into(), "junoswap");
1267
1268            let (unchecked_pool_id, _) = new_entry;
1269
1270            // asset_count * (asset_count - 1)
1271            // Total pairs = 5 * (5 - 1) = 20
1272            let expected_pairing_count = 20;
1273
1274            let actual_pairings = load_asset_pairings(&deps.storage)?;
1275            assert_eq!(actual_pairings.len(), expected_pairing_count);
1276
1277            for (_pairing, ref_vec) in actual_pairings {
1278                assert_eq!(ref_vec.len(), 1);
1279                // check the pool id is correct
1280                assert_eq!(
1281                    UncheckedPoolAddress::from(&ref_vec[0].pool_address),
1282                    unchecked_pool_id
1283                );
1284            }
1285
1286            Ok(())
1287        }
1288
1289        #[coverage_helper::test]
1290        fn add_pool_fails_without_registering_dex() -> AnsHostTestResult {
1291            let mut deps = mock_dependencies();
1292            mock_init(&mut deps).unwrap();
1293            let abstr = AbstractMockAddrs::new(deps.api);
1294
1295            let unregistered_dex = "unregistered";
1296
1297            let pool_assets = vec!["juno".into(), "osmo".into()];
1298            let metadata = pool_metadata(unregistered_dex, PoolType::Weighted, pool_assets.clone());
1299            // Register the assets in ANS
1300            register_assets_helper(&mut deps, pool_assets, &abstr.owner)?;
1301
1302            let entry = unchecked_pool_map_entry(&deps.api.addr_make("xxxx"), metadata);
1303
1304            let res = execute_update(&mut deps, (vec![entry], vec![]), &abstr.owner);
1305
1306            assert_eq!(
1307                res,
1308                Err(AnsHostError::UnregisteredDex {
1309                    dex: unregistered_dex.into(),
1310                })
1311            );
1312
1313            let actual_pools = load_pool_metadata(&deps.storage)?;
1314            assert!(actual_pools.is_empty());
1315
1316            Ok(())
1317        }
1318
1319        // THis test is weird because we remove the same one that is just created in this call
1320        #[coverage_helper::test]
1321        fn add_and_remove_same_pool() -> AnsHostTestResult {
1322            let mut deps = mock_dependencies();
1323            mock_init(&mut deps).unwrap();
1324            let abstr = AbstractMockAddrs::new(deps.api);
1325
1326            let dex = "junoswap";
1327
1328            let pool_assets = vec!["juno".into(), "osmo".into()];
1329            let metadata = pool_metadata(dex, PoolType::Weighted, pool_assets.clone());
1330
1331            // Register the assets in ANS
1332            register_assets_helper(&mut deps, pool_assets, &abstr.owner)?;
1333            register_dex(&mut deps, dex, &abstr.owner)?;
1334
1335            let entry = unchecked_pool_map_entry(&deps.api.addr_make("xxxx"), metadata);
1336
1337            execute_update(
1338                &mut deps,
1339                (vec![entry], vec![INITIAL_UNIQUE_POOL_ID.into()]),
1340                &abstr.owner,
1341            )?;
1342
1343            // metadata should be emtpy
1344            let actual_pools = load_pool_metadata(&deps.storage)?;
1345            assert!(actual_pools.is_empty());
1346
1347            // all pairs should be empty
1348            let actual_pairs = load_asset_pairings(&deps.storage)?;
1349            assert!(actual_pairs.is_empty());
1350
1351            Ok(())
1352        }
1353
1354        #[coverage_helper::test]
1355        fn remove_nonexistent_pool() -> AnsHostTestResult {
1356            let mut deps = mock_dependencies();
1357            mock_init(&mut deps).unwrap();
1358            let abstr = AbstractMockAddrs::new(deps.api);
1359
1360            let res = execute_update(
1361                &mut deps,
1362                (vec![], vec![INITIAL_UNIQUE_POOL_ID.into()]),
1363                &abstr.owner,
1364            );
1365
1366            assert!(res.is_ok());
1367
1368            // metadata should be empty
1369            let actual_pools = load_pool_metadata(&deps.storage)?;
1370            assert!(actual_pools.is_empty());
1371
1372            // all pairs should be empty
1373            let actual_pairs = load_asset_pairings(&deps.storage)?;
1374            assert!(actual_pairs.is_empty());
1375
1376            Ok(())
1377        }
1378
1379        #[coverage_helper::test]
1380        fn unregistered_assets_fail() -> AnsHostTestResult {
1381            let mut deps = mock_dependencies();
1382            mock_init(&mut deps).unwrap();
1383            let abstr = AbstractMockAddrs::new(deps.api);
1384
1385            let dex = "junoswap";
1386
1387            let metadata =
1388                pool_metadata(dex, PoolType::Weighted, vec!["juno".into(), "osmo".into()]);
1389
1390            register_dex(&mut deps, dex, &abstr.owner)?;
1391
1392            let entry = unchecked_pool_map_entry(&deps.api.addr_make("xxxx"), metadata);
1393
1394            let res = execute_update(&mut deps, (vec![entry], vec![]), &abstr.owner);
1395
1396            assert_eq!(
1397                res,
1398                Err(AnsHostError::UnregisteredAsset {
1399                    asset: "juno".to_string(),
1400                }),
1401            );
1402
1403            Ok(())
1404        }
1405    }
1406
1407    mod validate_pool_assets {
1408        use super::*;
1409
1410        #[coverage_helper::test]
1411        fn too_few() {
1412            let assets = &mut [];
1413            let deps = mock_dependencies();
1414            let result = validate_pool_assets(&deps.storage, assets).unwrap_err();
1415            assert_eq!(
1416                result,
1417                InvalidAssetCount {
1418                    min: MIN_POOL_ASSETS,
1419                    max: MAX_POOL_ASSETS,
1420                    provided: 0,
1421                }
1422            );
1423
1424            let assets = &mut ["a".into()];
1425            let deps = mock_dependencies();
1426            let result = validate_pool_assets(&deps.storage, assets).unwrap_err();
1427            assert_eq!(
1428                result,
1429                InvalidAssetCount {
1430                    min: MIN_POOL_ASSETS,
1431                    max: MAX_POOL_ASSETS,
1432                    provided: 1,
1433                }
1434            );
1435        }
1436
1437        #[coverage_helper::test]
1438        fn unregistered() {
1439            let mut assets = vec!["a".into(), "b".into()];
1440            let deps = mock_dependencies();
1441            let res = validate_pool_assets(&deps.storage, &mut assets);
1442
1443            assert_eq!(
1444                res,
1445                Err(AnsHostError::UnregisteredAsset {
1446                    asset: "a".to_string(),
1447                })
1448            );
1449        }
1450
1451        #[coverage_helper::test]
1452        fn valid_amounts() {
1453            let mut assets = vec!["a".into(), "b".into()];
1454            let mut deps = mock_dependencies();
1455            let abstr = AbstractMockAddrs::new(deps.api);
1456
1457            mock_init(&mut deps).unwrap();
1458            register_assets_helper(&mut deps, assets.clone(), &abstr.owner).unwrap();
1459
1460            let res = validate_pool_assets(&deps.storage, &mut assets);
1461
1462            assert!(res.is_ok());
1463
1464            let mut assets: Vec<AssetEntry> = vec!["a", "b", "c", "d", "e"]
1465                .into_iter()
1466                .map(|s| s.into())
1467                .collect();
1468
1469            register_assets_helper(&mut deps, assets.clone(), &abstr.owner).unwrap();
1470            let res = validate_pool_assets(&deps.storage, &mut assets);
1471
1472            assert!(res.is_ok());
1473        }
1474
1475        #[coverage_helper::test]
1476        fn too_many() {
1477            let mut assets: Vec<AssetEntry> = vec!["a", "b", "c", "d", "e", "f"]
1478                .into_iter()
1479                .map(|s| s.into())
1480                .collect();
1481            let deps = mock_dependencies();
1482            let result = validate_pool_assets(&deps.storage, &mut assets).unwrap_err();
1483            assert_eq!(
1484                result,
1485                InvalidAssetCount {
1486                    min: MIN_POOL_ASSETS,
1487                    max: MAX_POOL_ASSETS,
1488                    provided: 6,
1489                }
1490            );
1491        }
1492    }
1493}