abstract_client/
publisher.rs

1//! # Represents Abstract Publisher
2//!
3//! [`Publisher`] is an Account with helpers for publishing and maintaining Abstract Applications and Adapters
4
5use abstract_interface::{
6    AdapterDeployer, AppDeployer, DeployStrategy, RegisteredModule, RegistryQueryFns,
7    ServiceDeployer, StandaloneDeployer,
8};
9use cw_orch::{contract::Contract, prelude::*};
10use serde::Serialize;
11
12use crate::{
13    account::Account, client::AbstractClientResult, infrastructure::Infrastructure,
14    AbstractClientError, Environment,
15};
16use abstract_std::registry::NamespaceResponse;
17
18/// A Publisher represents an account that owns a namespace with the goal of publishing modules to the on-chain module-store.
19pub struct Publisher<Chain: CwEnv> {
20    account: Account<Chain>,
21}
22
23impl<Chain: CwEnv> Publisher<Chain> {
24    /// New publisher from account. We check that the account has an associated namespace.
25    /// If you create a publisher from an account without a namespace, use [`Self::new_with_namespace`] to claim it
26    pub fn new(account: &Account<Chain>) -> AbstractClientResult<Self> {
27        let namespace = account
28            .infrastructure()?
29            .registry
30            .namespaces(vec![account.id()?])?;
31
32        if namespace.namespaces.is_empty() {
33            return Err(AbstractClientError::NoNamespace {
34                account: account.id()?,
35            });
36        }
37
38        Ok(Self {
39            account: account.clone(),
40        })
41    }
42
43    /// New publisher from account and namespace
44    /// Claim the namespace from the account
45    pub fn new_with_namespace(
46        account: Account<Chain>,
47        namespace: &str,
48    ) -> AbstractClientResult<Self> {
49        let namespace_response = account
50            .infrastructure()?
51            .registry
52            .namespace(namespace.try_into()?)?;
53
54        if let NamespaceResponse::Claimed(namespace_info) = namespace_response {
55            if namespace_info.account_id != account.id()? {
56                return Err(AbstractClientError::NamespaceClaimed {
57                    namespace: namespace.to_string(),
58                    account_id: namespace_info.account_id,
59                });
60            }
61        } else {
62            account.claim_namespace(namespace)?;
63        }
64
65        Ok(Self { account })
66    }
67
68    /// Publish an Abstract App
69    pub fn publish_app<
70        M: ContractInstance<Chain> + RegisteredModule + From<Contract<Chain>> + AppDeployer<Chain>,
71    >(
72        &self,
73    ) -> AbstractClientResult<()> {
74        let contract = Contract::new(M::module_id().to_owned(), self.account.environment());
75        let app: M = contract.into();
76        app.deploy(M::module_version().parse()?, DeployStrategy::Try)
77            .map_err(Into::into)
78    }
79
80    /// Publish an Abstract Standalone
81    pub fn publish_standalone<
82        M: ContractInstance<Chain>
83            + RegisteredModule
84            + From<Contract<Chain>>
85            + StandaloneDeployer<Chain>,
86    >(
87        &self,
88    ) -> AbstractClientResult<()> {
89        let contract = Contract::new(M::module_id().to_owned(), self.account.environment());
90        let standalone: M = contract.into();
91        standalone
92            .deploy(M::module_version().parse()?, DeployStrategy::Try)
93            .map_err(Into::into)
94    }
95
96    /// Publish an Abstract Adapter
97    pub fn publish_adapter<
98        CustomInitMsg: Serialize,
99        M: ContractInstance<Chain>
100            + RegisteredModule
101            + From<Contract<Chain>>
102            + AdapterDeployer<Chain, CustomInitMsg>,
103    >(
104        &self,
105        init_msg: CustomInitMsg,
106    ) -> AbstractClientResult<M> {
107        let contract = Contract::new(M::module_id().to_owned(), self.account.environment());
108        let adapter: M = contract.into();
109        adapter.deploy(M::module_version().parse()?, init_msg, DeployStrategy::Try)?;
110        Ok(adapter)
111    }
112
113    /// Publish an Abstract Service
114    pub fn publish_service<
115        M: ContractInstance<Chain> + RegisteredModule + From<Contract<Chain>> + ServiceDeployer<Chain>,
116    >(
117        &self,
118        init_msg: &<M as InstantiableContract>::InstantiateMsg,
119    ) -> AbstractClientResult<()> {
120        let contract = Contract::new(M::module_id().to_owned(), self.account.environment());
121        let service: M = contract.into();
122        service
123            .deploy(M::module_version().parse()?, init_msg, DeployStrategy::Try)
124            .map_err(Into::into)
125    }
126
127    /// Abstract Account of the publisher
128    pub fn account(&self) -> &Account<Chain> {
129        &self.account
130    }
131}