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
23pub 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
52pub 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 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 let addr = deps.as_ref().api.addr_validate(&new_address)?;
70
71 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
84pub 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 cw_ownable::assert_owner(deps.storage, &msg_info.sender)?;
93
94 for (name, new_asset) in to_add.into_iter() {
95 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
116pub fn update_channels(
118 deps: DepsMut,
119 msg_info: MessageInfo,
120 to_add: Vec<(UncheckedChannelEntry, String)>,
121 to_remove: Vec<UncheckedChannelEntry>,
122) -> AnsHostResult {
123 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 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
141fn update_dex_registry(
143 deps: DepsMut,
144 msg_info: MessageInfo,
145 to_add: Vec<String>,
146 to_remove: Vec<String>,
147) -> AnsHostResult {
148 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 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 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_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 next_unique_pool_id.increment();
214 }
215
216 for pool_id_to_remove in to_remove {
217 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 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 POOL_METADATA.remove(deps.storage, pool_id_to_remove);
235 }
236
237 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
248fn 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 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
289fn 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
306fn 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 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 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
335fn validate_pool_assets(
337 storage: &dyn Storage,
338 assets: &mut [AssetEntry],
339) -> Result<(), AnsHostError> {
340 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 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 #[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 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 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 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 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 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 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 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 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_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_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 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 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_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 #[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_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 let actual_pools = load_pool_metadata(&deps.storage)?;
1345 assert!(actual_pools.is_empty());
1346
1347 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 let actual_pools = load_pool_metadata(&deps.storage)?;
1370 assert!(actual_pools.is_empty());
1371
1372 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}