abstract_client/
account.rs

1//! # Represents Abstract Account
2//!
3//! [`Account`] allows you to interact with your or another user Abstract Account
4//!
5//! Example of creating an account
6//! ```
7//! # use abstract_client::AbstractClientError;
8//! # use abstract_testing::prelude::*;
9//! use abstract_client::{AbstractClient, Account};
10//! use cw_orch::prelude::*;
11//!
12//! # let chain = MockBech32::new("mock");
13//! # let client: AbstractClient<MockBech32> = AbstractClient::builder(chain.clone()).build()?;
14//!
15//! let alice_account: Account<MockBech32> = client
16//!     .account_builder()
17//!     .name("Alice")
18//!     .build()?;
19//!
20//! assert_eq!(alice_account.owner()?, client.sender());
21//! # Ok::<(), AbstractClientError>(())
22//! ```
23use std::fmt::{Debug, Display};
24
25use abstract_interface::{
26    Abstract, AbstractInterfaceError, AccountDetails, AccountExecFns, AccountI, AccountQueryFns,
27    DependencyCreation, IbcClient, InstallConfig, MFactoryQueryFns, RegisteredModule,
28    RegistryQueryFns,
29};
30use abstract_std::{
31    account,
32    account::{
33        state::AccountInfo, AccountModuleInfo, InfoResponse, ModuleAddressesResponse,
34        ModuleInfosResponse, ModuleInstallConfig,
35    },
36    objects::{
37        gov_type::GovernanceDetails,
38        module::{ModuleId, ModuleInfo, ModuleVersion},
39        namespace::Namespace,
40        ownership,
41        validation::verifiers,
42        AccountId,
43    },
44    registry::{self, NamespaceResponse},
45    IBC_CLIENT,
46};
47use cosmwasm_std::{to_json_binary, Coins, CosmosMsg, Uint128};
48use cw_orch::{
49    contract::Contract,
50    environment::{Environment as _, MutCwEnv},
51    prelude::*,
52};
53use serde::{de::DeserializeOwned, Serialize};
54
55use crate::{
56    client::AbstractClientResult, infrastructure::Infrastructure, AbstractClientError, Application,
57    Environment, Publisher,
58};
59
60/// A builder for creating [`Accounts`](Account).
61/// Get the builder from the [`AbstractClient::account_builder`](crate::AbstractClient)
62/// and create the account with the `build` method.
63///
64/// ```
65/// # use cw_orch::prelude::*;
66/// # use abstract_client::{AbstractClientError, Environment};
67/// # let chain = MockBech32::new("mock");
68/// # let abstr_client = abstract_client::AbstractClient::builder(chain.clone()).build().unwrap();
69/// # let chain = abstr_client.environment();
70/// use abstract_client::{AbstractClient, Account};
71///
72/// let client = AbstractClient::new(chain)?;
73/// let account: Account<MockBech32> = client.account_builder()
74///     .name("alice")
75///     // other account configuration
76///     .build()?;
77/// # Ok::<(), AbstractClientError>(())
78/// ```
79pub struct AccountBuilder<'a, Chain: CwEnv> {
80    pub(crate) abstr: Abstract<Chain>,
81    name: Option<String>,
82    description: Option<String>,
83    link: Option<String>,
84    namespace: Option<Namespace>,
85    ownership: Option<GovernanceDetails<String>>,
86    owner_account: Option<&'a Account<Chain>>,
87    install_modules: Vec<ModuleInstallConfig>,
88    enable_ibc: bool,
89    funds: AccountCreationFunds,
90    expected_local_account_id: Option<u32>,
91}
92
93/// Creation funds
94enum AccountCreationFunds {
95    #[allow(clippy::type_complexity)]
96    Auto(Box<dyn Fn(&[Coin]) -> bool>),
97    Coins(Coins),
98}
99
100impl<'a, Chain: CwEnv> AccountBuilder<'a, Chain> {
101    pub(crate) fn new(abstr: &Abstract<Chain>) -> Self {
102        Self {
103            abstr: abstr.clone(),
104            name: None,
105            description: None,
106            link: None,
107            namespace: None,
108            ownership: None,
109            owner_account: None,
110            install_modules: vec![],
111            enable_ibc: false,
112            funds: AccountCreationFunds::Coins(Coins::default()),
113            expected_local_account_id: None,
114        }
115    }
116
117    /// Username for the account
118    /// Defaults to "Default Abstract Account"
119    pub fn name(&mut self, name: impl Into<String>) -> &mut Self {
120        self.name = Some(name.into());
121        self
122    }
123
124    /// Description for the account
125    pub fn description(&mut self, description: impl Into<String>) -> &mut Self {
126        self.description = Some(description.into());
127        self
128    }
129
130    /// http(s) or ipfs link for the account
131    pub fn link(&mut self, link: impl Into<String>) -> &mut Self {
132        self.link = Some(link.into());
133        self
134    }
135
136    /// Unique namespace for the account
137    /// Setting this will claim the namespace for the account on construction.
138    pub fn namespace(&mut self, namespace: Namespace) -> &mut Self {
139        self.namespace = Some(namespace);
140        self
141    }
142
143    /// Create sub-account
144    pub fn sub_account(&mut self, owner_account: &'a Account<Chain>) -> &mut Self {
145        self.owner_account = Some(owner_account);
146        self
147    }
148
149    /// Governance of the account.
150    /// Defaults to the [`GovernanceDetails::Monarchy`] variant, owned by the sender
151    pub fn ownership(&mut self, ownership: GovernanceDetails<String>) -> &mut Self {
152        self.ownership = Some(ownership);
153        self
154    }
155
156    /// Install an adapter on current account.
157    pub fn install_adapter<M: InstallConfig<InitMsg = Empty>>(&mut self) -> &mut Self {
158        self.install_modules
159            .push(M::install_config(&Empty {}).unwrap());
160        self
161    }
162
163    /// Install an application on current account.
164    pub fn install_app<M: InstallConfig>(&mut self, configuration: &M::InitMsg) -> &mut Self {
165        self.install_modules
166            .push(M::install_config(configuration).unwrap());
167        self
168    }
169
170    /// Install an standalone on current account.
171    pub fn install_standalone<M: InstallConfig>(
172        &mut self,
173        configuration: &M::InitMsg,
174    ) -> &mut Self {
175        self.install_modules
176            .push(M::install_config(configuration).unwrap());
177        self
178    }
179
180    /// Install an service on current account.
181    pub fn install_service<M: InstallConfig>(&mut self, configuration: &M::InitMsg) -> &mut Self {
182        self.install_modules
183            .push(M::install_config(configuration).unwrap());
184        self
185    }
186
187    /// Install an application with dependencies on current account.
188    pub fn install_app_with_dependencies<M: DependencyCreation + InstallConfig>(
189        &mut self,
190        module_configuration: &M::InitMsg,
191        dependencies_config: M::DependenciesConfig,
192    ) -> &mut Self {
193        let deps_install_config = M::dependency_install_configs(dependencies_config).unwrap();
194        self.install_modules.extend(deps_install_config);
195        self.install_modules
196            .push(M::install_config(module_configuration).unwrap());
197        self
198    }
199
200    /// Install an standalone with dependencies on current account.
201    pub fn install_standalone_with_dependencies<M: DependencyCreation + InstallConfig>(
202        &mut self,
203        module_configuration: &M::InitMsg,
204        dependencies_config: M::DependenciesConfig,
205    ) -> &mut Self {
206        let deps_install_config = M::dependency_install_configs(dependencies_config).unwrap();
207        self.install_modules.extend(deps_install_config);
208        self.install_modules
209            .push(M::install_config(module_configuration).unwrap());
210        self
211    }
212
213    /// Install unchecked modules on current account.
214    ///
215    /// Since this method allows to pass unchecked `ModuleInstallConfig` \
216    /// calling this method should be avoided unless it's the only way to install module, for example no `InstallModule` implemented for the module
217    pub fn with_modules(
218        &mut self,
219        install_modules: impl IntoIterator<Item = ModuleInstallConfig>,
220    ) -> &mut Self {
221        self.install_modules.extend(install_modules);
222        self
223    }
224
225    /// Enable ibc on account. This parameter ignored if installation of `IbcClient`
226    /// already specified in `install_modules`.
227    pub fn enable_ibc(&mut self) -> &mut Self {
228        self.enable_ibc = true;
229        self
230    }
231
232    /// Enables automatically paying for module instantiations and namespace registration.
233    /// The provided function will be called with the required funds. If the function returns `false`,
234    /// the account creation will fail.
235    pub fn auto_fund_assert<F: Fn(&[Coin]) -> bool + 'static>(&mut self, f: F) -> &mut Self {
236        self.funds = AccountCreationFunds::Auto(Box::new(f));
237        self
238    }
239
240    /// Enables automatically paying for module instantiations and namespace registration.
241    /// Use `auto_fund_assert` to add limits to the auto fund mode.
242    pub fn auto_fund(&mut self) -> &mut Self {
243        self.funds = AccountCreationFunds::Auto(Box::new(|_| true));
244        self
245    }
246
247    /// Add funds to the account creation
248    /// Can't be used in pair with auto fund mode
249    pub fn funds(&mut self, funds: &[Coin]) -> AbstractClientResult<&mut Self> {
250        let coins = match &mut self.funds {
251            AccountCreationFunds::Auto(_) => return Err(AbstractClientError::FundsWithAutoFund {}),
252            AccountCreationFunds::Coins(coins) => coins,
253        };
254
255        for coin in funds {
256            coins
257                .add(coin.clone())
258                .map_err(AbstractInterfaceError::from)?;
259        }
260        Ok(self)
261    }
262
263    /// Assign expected local account_id on creation.
264    /// It's designed to be used in pair with [`crate::AbstractClient::random_account_id`].
265    /// The tx will error if this account id already claimed or it's less than 2147483648. Useful for instantiate2 address prediction.
266    pub fn expected_account_id(&mut self, local_account_id: u32) -> &mut Self {
267        self.expected_local_account_id = Some(local_account_id);
268        self
269    }
270
271    /// Builds the [`Account`].
272    pub fn build(&self) -> AbstractClientResult<Account<Chain>> {
273        let install_modules = self.install_modules();
274
275        let chain = self.abstr.registry.environment();
276        let sender = chain.sender_addr().to_string();
277        let name = self
278            .name
279            .clone()
280            .unwrap_or_else(|| String::from("Default Abstract Account"));
281        let ownership = self
282            .ownership
283            .clone()
284            .unwrap_or(GovernanceDetails::Monarchy { monarch: sender });
285
286        // Validate everything before sending tx
287        verifiers::validate_name(&name)?;
288        verifiers::validate_description(self.description.as_deref())?;
289        verifiers::validate_link(self.link.as_deref())?;
290
291        let funds = self.generate_funds(&install_modules, true)?;
292        let account_details = AccountDetails {
293            name,
294            description: self.description.clone(),
295            link: self.link.clone(),
296            namespace: self.namespace.as_ref().map(ToString::to_string),
297            install_modules,
298            account_id: self.expected_local_account_id,
299        };
300        let abstract_account = match self.owner_account {
301            None => AccountI::create(&self.abstr, account_details, ownership, &funds)?,
302            Some(owner_account) => owner_account
303                .abstr_account
304                .create_and_return_sub_account(account_details, &funds)?,
305        };
306        Ok(Account::new(abstract_account))
307    }
308
309    /// Modules to install without duplicates
310    fn install_modules(&self) -> Vec<ModuleInstallConfig> {
311        let mut install_modules = self.install_modules.clone();
312        if self.enable_ibc {
313            install_modules.push(IbcClient::<Chain>::install_config(&Empty {}).unwrap());
314        }
315        install_modules.dedup();
316        install_modules
317    }
318
319    fn generate_funds(
320        &self,
321        // Not using self.install_modules because it's maybe build on existent account and we pass only new modules here
322        install_modules: &[ModuleInstallConfig],
323        // Include funds for namespace claiming
324        include_namespace_claiming: bool,
325    ) -> AbstractClientResult<Vec<Coin>> {
326        let funds = match &self.funds {
327            AccountCreationFunds::Auto(auto_funds_assert) => {
328                let modules = install_modules.iter().map(|m| m.module.clone()).collect();
329                // Simulate module install to find out required funds
330                let simulate_response = self
331                    .abstr
332                    .module_factory
333                    .simulate_install_modules(modules)?;
334
335                let mut funds = Coins::try_from(simulate_response.total_required_funds).unwrap();
336
337                // Add namespace fee if any
338                if include_namespace_claiming && self.namespace.is_some() {
339                    let vc_config = self.abstr.registry.config()?;
340
341                    if let Some(namespace_fee) = vc_config.namespace_registration_fee {
342                        funds
343                            .add(namespace_fee)
344                            .map_err(AbstractInterfaceError::from)?;
345                    }
346                };
347
348                let funds = funds.into_vec();
349                // Use auto funds assert function for validation
350                if !auto_funds_assert(&funds) {
351                    return Err(AbstractClientError::AutoFundsAssertFailed(funds));
352                }
353                funds
354            }
355            AccountCreationFunds::Coins(coins) => coins.to_vec(),
356        };
357        Ok(funds)
358    }
359}
360
361/// Represents an existing Abstract account.
362///
363/// Get this struct from [`AbstractClient::account_from_namespace`](crate::AbstractClient)
364/// or create a new account with the [`AccountBuilder`](crate::AbstractClient::account_builder).
365#[derive(Clone)]
366pub struct Account<Chain: CwEnv> {
367    pub(crate) abstr_account: AccountI<Chain>,
368}
369
370impl<Chain: CwEnv> AsRef<AccountI<Chain>> for Account<Chain> {
371    fn as_ref(&self) -> &AccountI<Chain> {
372        &self.abstr_account
373    }
374}
375
376impl<Chain: CwEnv> Account<Chain> {
377    pub(crate) fn new(abstract_account: AccountI<Chain>) -> Self {
378        Self {
379            abstr_account: abstract_account,
380        }
381    }
382
383    pub(crate) fn maybe_from_namespace(
384        abstr: &Abstract<Chain>,
385        namespace: Namespace,
386    ) -> AbstractClientResult<Option<Self>> {
387        let namespace_response: NamespaceResponse = abstr.registry.namespace(namespace)?;
388
389        let NamespaceResponse::Claimed(info) = namespace_response else {
390            return Ok(None);
391        };
392
393        let abstract_account: AccountI<Chain> = AccountI::load_from(abstr, info.account_id)?;
394
395        Ok(Some(Self::new(abstract_account)))
396    }
397
398    /// Retrieves the [`Publisher`] object from an account
399    ///
400    /// This object is used to publish modules to Abstract
401    pub fn publisher(&self) -> AbstractClientResult<Publisher<Chain>> {
402        Publisher::new(self)
403    }
404
405    /// Get the [`AccountId`] of the Account
406    pub fn id(&self) -> AbstractClientResult<AccountId> {
407        self.abstr_account.id().map_err(Into::into)
408    }
409
410    /// Query account balance of a given denom
411    pub fn query_balance(&self, denom: impl Into<String>) -> AbstractClientResult<Uint128> {
412        let coins = self
413            .environment()
414            .bank_querier()
415            .balance(&self.address()?, Some(denom.into()))
416            .map_err(Into::into)?;
417
418        // There will always be a single element in this case.
419        Ok(coins[0].amount)
420    }
421
422    /// Query account balances of all denoms
423    pub fn query_balances(&self) -> AbstractClientResult<Vec<Coin>> {
424        self.environment()
425            .bank_querier()
426            .balance(&self.address()?, None)
427            .map_err(Into::into)
428            .map_err(Into::into)
429    }
430
431    /// Query account info
432    pub fn info(&self) -> AbstractClientResult<AccountInfo> {
433        let info_response: InfoResponse = self.abstr_account.info()?;
434        Ok(info_response.info)
435    }
436
437    /// Install an application on the account.
438    /// Errors if this module already installed
439    pub fn install_app<M: InstallConfig + From<Contract<Chain>>>(
440        &self,
441        configuration: &M::InitMsg,
442        funds: &[Coin],
443    ) -> AbstractClientResult<Application<Chain, M>> {
444        let modules = vec![M::install_config(configuration)?];
445
446        self.install_module_internal(modules, funds)
447    }
448
449    /// Install an standalone on the account.
450    /// if `install_on_sub_account` is `true`, the application will be installed on new a sub-account. (default)
451    /// Errors if this module already installed
452    pub fn install_standalone<M: InstallConfig + From<Contract<Chain>>>(
453        &self,
454        configuration: &M::InitMsg,
455        funds: &[Coin],
456    ) -> AbstractClientResult<Application<Chain, M>> {
457        let modules = vec![M::install_config(configuration)?];
458
459        self.install_module_internal(modules, funds)
460    }
461
462    /// Install an service on the account.
463    /// if `install_on_sub_account` is `true`, the application will be installed on new a sub-account.
464    /// Errors if this module already installed
465    pub fn install_service<M: InstallConfig + From<Contract<Chain>>>(
466        &self,
467        configuration: &M::InitMsg,
468        funds: &[Coin],
469    ) -> AbstractClientResult<Application<Chain, M>> {
470        let modules = vec![M::install_config(configuration)?];
471
472        self.install_module_internal(modules, funds)
473    }
474
475    /// Install an adapter on current account.
476    /// Errors if this module already installed
477    pub fn install_adapter<M: InstallConfig<InitMsg = Empty> + From<Contract<Chain>>>(
478        &self,
479        funds: &[Coin],
480    ) -> AbstractClientResult<Application<Chain, M>> {
481        let modules = vec![M::install_config(&Empty {})?];
482
483        self.install_module_internal(modules, funds)
484    }
485
486    /// Creates a new sub-account on the current account and
487    /// installs an App module and its dependencies with the provided dependencies config. \
488    ///
489    /// The returned [`Application`] is a wrapper around the sub-account and simplifies interaction with the App module.
490    /// Errors if this module installed
491    /// FIXME: Errors if any of the dependencies modules already installed
492    pub fn install_app_with_dependencies<
493        M: DependencyCreation + InstallConfig + From<Contract<Chain>>,
494    >(
495        &self,
496        module_configuration: &M::InitMsg,
497        dependencies_config: M::DependenciesConfig,
498        funds: &[Coin],
499    ) -> AbstractClientResult<Application<Chain, M>> {
500        let mut install_configs: Vec<ModuleInstallConfig> =
501            M::dependency_install_configs(dependencies_config)?;
502        install_configs.push(M::install_config(module_configuration)?);
503
504        self.install_module_internal(install_configs, funds)
505    }
506
507    /// Creates a new sub-account on the current account and
508    /// installs an Standalone module and its dependencies with the provided dependencies config. \
509    ///
510    /// The returned [`Application`] is a wrapper around the sub-account and simplifies interaction with the Standalone module.
511    pub fn install_standalone_with_dependencies<
512        M: DependencyCreation + InstallConfig + From<Contract<Chain>>,
513    >(
514        &self,
515        module_configuration: &M::InitMsg,
516        dependencies_config: M::DependenciesConfig,
517        funds: &[Coin],
518    ) -> AbstractClientResult<Application<Chain, M>> {
519        let mut install_configs: Vec<ModuleInstallConfig> =
520            M::dependency_install_configs(dependencies_config)?;
521        install_configs.push(M::install_config(module_configuration)?);
522
523        self.install_module_internal(install_configs, funds)
524    }
525
526    /// Upgrades the account to the latest version.
527    pub fn upgrade(&self, version: ModuleVersion) -> AbstractClientResult<Chain::Response> {
528        self.abstr_account
529            .upgrade(vec![(
530                ModuleInfo::from_id(abstract_std::constants::ACCOUNT, version.clone())?,
531                Some(
532                    to_json_binary(&abstract_std::account::MigrateMsg { code_id: None })
533                        .map_err(Into::<CwOrchError>::into)?,
534                ),
535            )])
536            .map_err(Into::into)
537    }
538
539    /// Returns owner of the account
540    pub fn ownership(&self) -> AbstractClientResult<ownership::Ownership<String>> {
541        self.abstr_account.ownership().map_err(Into::into)
542    }
543
544    /// Returns the owner address of the account.
545    /// If the account is a sub-account, it will return the top-level owner address.
546    pub fn owner(&self) -> AbstractClientResult<Addr> {
547        self.abstr_account
548            .top_level_owner()
549            .map(|tlo| tlo.address)
550            .map_err(Into::into)
551    }
552
553    /// Executes a [`CosmosMsg`] on the account.
554    pub fn execute(
555        &self,
556        execute_msgs: impl IntoIterator<Item = impl Into<CosmosMsg>>,
557        funds: &[Coin],
558    ) -> AbstractClientResult<Chain::Response> {
559        let msgs = execute_msgs.into_iter().map(Into::into).collect();
560        self.configure(&account::ExecuteMsg::Execute { msgs }, funds)
561    }
562
563    /// Executes a [`account::ExecuteMsg`] on the account.
564    pub fn configure(
565        &self,
566        execute_msg: &account::ExecuteMsg,
567        funds: &[Coin],
568    ) -> AbstractClientResult<Chain::Response> {
569        self.abstr_account
570            .execute(execute_msg, funds)
571            .map_err(Into::into)
572    }
573
574    /// Queries a module on the account.
575    pub fn query_module<Q: Serialize + Debug, T: Serialize + DeserializeOwned>(
576        &self,
577        module_id: ModuleId,
578        msg: &Q,
579    ) -> AbstractClientResult<T> {
580        let mut module_address_response = self.module_addresses(vec![module_id.to_owned()])?;
581        let (_, module_addr) = module_address_response.modules.pop().unwrap();
582        let response = self
583            .environment()
584            .query(msg, &module_addr)
585            .map_err(Into::into)?;
586        Ok(response)
587    }
588
589    /// Set IBC status on an Account.
590    pub fn set_ibc_status(&self, enabled: bool) -> AbstractClientResult<Chain::Response> {
591        self.abstr_account
592            .set_ibc_status(enabled)
593            .map_err(Into::into)
594    }
595
596    /// Module infos of installed modules on account
597    pub fn module_infos(&self) -> AbstractClientResult<ModuleInfosResponse> {
598        let mut module_infos: Vec<AccountModuleInfo> = vec![];
599        loop {
600            let last_module_id: Option<String> = module_infos
601                .last()
602                .map(|module_info| module_info.id.clone());
603            let res: ModuleInfosResponse = self.abstr_account.module_infos(None, last_module_id)?;
604            if res.module_infos.is_empty() {
605                break;
606            }
607            module_infos.extend(res.module_infos);
608        }
609        Ok(ModuleInfosResponse { module_infos })
610    }
611
612    /// Addresses of installed modules on account
613    pub fn module_addresses(
614        &self,
615        ids: Vec<String>,
616    ) -> AbstractClientResult<ModuleAddressesResponse> {
617        self.abstr_account.module_addresses(ids).map_err(Into::into)
618    }
619
620    /// Check if module installed on account
621    pub fn module_installed(&self, id: ModuleId) -> AbstractClientResult<bool> {
622        // Currently this is the only way that
623        // - Doesn't error on missing account module
624        // - Predictable gas usage
625        let key = account::state::ACCOUNT_MODULES.key(id).to_vec();
626        let maybe_module_addr = self
627            .environment()
628            .wasm_querier()
629            .raw_query(&self.abstr_account.address()?, key)
630            .map_err(Into::into)?;
631        Ok(!maybe_module_addr.is_empty())
632    }
633
634    /// Check if module version installed on account
635    pub fn module_version_installed(&self, module: ModuleInfo) -> AbstractClientResult<bool> {
636        let module_id = module.id();
637        // First we need to verify it's installed or next query will fail with strange error
638        if !self.module_installed(&module_id)? {
639            return Ok(false);
640        }
641
642        let mut module_versions_response = self.abstr_account.module_versions(vec![module_id])?;
643        let installed_version = module_versions_response.versions.pop().unwrap().version;
644        let expected_version = match &module.version {
645            // If latest we need to find latest version stored in Registry
646            ModuleVersion::Latest => {
647                let account_config = self.abstr_account.config()?;
648                let mut modules_response: registry::ModulesResponse = self
649                    .environment()
650                    .query(
651                        &registry::QueryMsg::Modules {
652                            infos: vec![module.clone()],
653                        },
654                        &account_config.registry_address,
655                    )
656                    .map_err(Into::into)?;
657                modules_response
658                    .modules
659                    .pop()
660                    .unwrap()
661                    .module
662                    .info
663                    .version
664                    .to_string()
665            }
666            ModuleVersion::Version(version) => version.clone(),
667        };
668        Ok(installed_version == expected_version)
669    }
670
671    /// Check if module installed on account
672    pub fn ibc_status(&self) -> AbstractClientResult<bool> {
673        self.module_installed(IBC_CLIENT)
674    }
675
676    /// Builder to create a subaccount from this account
677    pub fn sub_account_builder(&self) -> AccountBuilder<Chain> {
678        let mut builder = AccountBuilder::new(&self.infrastructure().unwrap());
679        builder.sub_account(self);
680        builder.name("Sub Account");
681        builder
682    }
683
684    /// Get Sub Accounts of this account
685    pub fn sub_accounts(&self) -> AbstractClientResult<Vec<Account<Chain>>> {
686        let mut sub_accounts = vec![];
687        let mut start_after = None;
688        let abstr_deployment = Abstract::load_from(self.environment())?;
689        loop {
690            let sub_account_ids = self
691                .abstr_account
692                .sub_account_ids(None, start_after)?
693                .sub_accounts;
694            start_after = sub_account_ids.last().cloned();
695
696            if sub_account_ids.is_empty() {
697                break;
698            }
699            sub_accounts.extend(sub_account_ids);
700        }
701
702        let sub_accounts = sub_accounts
703            .into_iter()
704            .map(|id| {
705                Ok(Account::new(AccountI::load_from(
706                    &abstr_deployment,
707                    AccountId::local(id),
708                )?))
709            })
710            .collect::<Result<Vec<_>, AbstractInterfaceError>>();
711
712        Ok(sub_accounts?)
713    }
714
715    /// Address of the account
716    pub fn address(&self) -> AbstractClientResult<Addr> {
717        Ok(self.abstr_account.address()?)
718    }
719
720    /// Retrieve installed application on account
721    /// This can't retrieve sub-account installed applications.
722    pub fn application<M: RegisteredModule + From<Contract<Chain>>>(
723        &self,
724    ) -> AbstractClientResult<Application<Chain, M>> {
725        let module = self.module()?;
726        let account = self.clone();
727
728        Application::new(account, module)
729    }
730
731    /// Install module on current account
732    fn install_module_internal<M: RegisteredModule + From<Contract<Chain>>>(
733        &self,
734        mut modules: Vec<ModuleInstallConfig>,
735        funds: &[Coin],
736    ) -> AbstractClientResult<Application<Chain, M>> {
737        let module_infos = self.module_infos()?;
738        modules.retain(|m| {
739            !module_infos
740                .module_infos
741                .iter()
742                .any(|module_info| module_info.id == m.module.id())
743        });
744        if !modules.is_empty() {
745            self.abstr_account.install_modules(modules, funds)?;
746        }
747
748        let module = self.module::<M>()?;
749
750        Application::new(Account::new(self.abstr_account.clone()), module)
751    }
752
753    pub(crate) fn module<T: RegisteredModule + From<Contract<Chain>>>(
754        &self,
755    ) -> AbstractClientResult<T> {
756        let module_id = T::module_id();
757        let account_module_id = T::installed_module_contract_id(&self.id()?);
758        let maybe_module_addr = self.module_addresses(vec![module_id.to_string()])?.modules;
759
760        if !maybe_module_addr.is_empty() {
761            let contract = Contract::new(account_module_id, self.environment());
762            contract.set_address(&maybe_module_addr[0].1);
763            let module: T = contract.into();
764            Ok(module)
765        } else {
766            Err(AbstractClientError::ModuleNotInstalled {})
767        }
768    }
769
770    /// Claim a namespace for an existing account
771    pub fn claim_namespace(
772        &self,
773        namespace: impl Into<String>,
774    ) -> Result<Chain::Response, AbstractInterfaceError> {
775        self.abstr_account.claim_namespace(namespace)
776    }
777}
778
779impl<Chain: MutCwEnv> Account<Chain> {
780    /// Set balance for the Account
781    pub fn set_balance(&self, amount: &[Coin]) -> AbstractClientResult<()> {
782        self.environment()
783            .set_balance(&self.address()?, amount.to_vec())
784            .map_err(Into::into)
785            .map_err(Into::into)
786    }
787
788    /// Add balance to the Account
789    pub fn add_balance(&self, amount: &[Coin]) -> AbstractClientResult<()> {
790        self.environment()
791            .add_balance(&self.address()?, amount.to_vec())
792            .map_err(Into::into)
793            .map_err(Into::into)
794    }
795}
796
797impl<Chain: CwEnv> Display for Account<Chain> {
798    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
799        write!(f, "{}", self.abstr_account)
800    }
801}
802
803impl<Chain: CwEnv> Debug for Account<Chain> {
804    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
805        f.debug_struct("Account")
806            .field("abstr_account", &self.abstr_account)
807            .finish()
808    }
809}
810
811#[cfg(test)]
812pub mod test {
813    use abstract_interface::{Abstract, RegistryQueryFns};
814    use abstract_std::objects::namespace::Namespace;
815    use cw_orch::{contract::Deploy, mock::MockBech32};
816
817    use crate::AbstractClient;
818
819    #[coverage_helper::test]
820    fn namespace_after_creation() -> cw_orch::anyhow::Result<()> {
821        let mock = MockBech32::new("mock");
822        let abstr = AbstractClient::builder(mock.clone()).build()?;
823
824        let my_namespace = "my-namespace";
825        let new_account = abstr.account_builder().build()?;
826        new_account.claim_namespace(my_namespace)?;
827
828        // Verify the namespace exists
829        let abstr = Abstract::load_from(mock.clone())?;
830        let namespace_response = abstr.registry.namespace(Namespace::new(my_namespace)?)?;
831
832        match namespace_response {
833            abstract_std::registry::NamespaceResponse::Claimed(c) => {
834                assert_eq!(c.account_id, new_account.id()?)
835            }
836            abstract_std::registry::NamespaceResponse::Unclaimed {} => {
837                panic!("Expected claimed namespace")
838            }
839        }
840
841        Ok(())
842    }
843}