abstract_interface/
deployment.rs

1use cosmwasm_std::{instantiate2_address, Binary, CanonicalAddr, Instantiate2AddressError};
2use cw_blob::interface::{CwBlob, DeterministicInstantiation};
3#[cfg(feature = "daemon")]
4use cw_orch::daemon::DeployedChains;
5
6use cw_orch::prelude::*;
7
8use crate::{
9    get_ibc_contracts, get_native_contracts, AbstractIbc, AbstractInterfaceError, AccountI,
10    AnsHost, ModuleFactory, Registry,
11};
12use abstract_std::{
13    native_addrs,
14    objects::{gov_type::GovernanceDetails, module::ModuleInfo},
15    registry::QueryMsgFns,
16    ACCOUNT, ANS_HOST, MODULE_FACTORY, REGISTRY,
17};
18
19const CW_BLOB: &str = "cw:blob";
20
21#[derive(Clone)]
22pub struct Abstract<Chain: CwEnv> {
23    pub ans_host: AnsHost<Chain>,
24    pub registry: Registry<Chain>,
25    pub module_factory: ModuleFactory<Chain>,
26    pub ibc: AbstractIbc<Chain>,
27    pub(crate) account: AccountI<Chain>,
28    pub(crate) blob: CwBlob<Chain>,
29}
30
31impl<Chain: CwEnv> Deploy<Chain> for Abstract<Chain> {
32    // We don't have a custom error type
33    type Error = AbstractInterfaceError;
34    type DeployData = ();
35
36    fn store_on(chain: Chain) -> Result<Self, AbstractInterfaceError> {
37        let blob = CwBlob::new(CW_BLOB, chain.clone());
38
39        let ans_host = AnsHost::new(ANS_HOST, chain.clone());
40        let registry = Registry::new(REGISTRY, chain.clone());
41        let module_factory = ModuleFactory::new(MODULE_FACTORY, chain.clone());
42        let account = AccountI::new(ACCOUNT, chain.clone());
43
44        let ibc_infra = AbstractIbc::new(&chain);
45
46        blob.upload_if_needed()?;
47        ans_host.upload()?;
48        registry.upload()?;
49        module_factory.upload()?;
50        account.upload()?;
51        ibc_infra.upload()?;
52
53        let deployment = Abstract {
54            ans_host,
55            registry,
56            module_factory,
57            account,
58            ibc: ibc_infra,
59            blob,
60        };
61
62        Ok(deployment)
63    }
64
65    /// Deploys abstract using provided [`TxHandler::Sender`].
66    /// After deployment sender of abstract contracts is a sender of provided `chain`
67    fn deploy_on(
68        chain: Chain,
69        _deploy_data: Self::DeployData,
70    ) -> Result<Self, AbstractInterfaceError> {
71        let sender_addr = chain.sender_addr();
72        let admin = sender_addr.to_string();
73        // upload
74        let deployment = Self::store_on(chain.clone())?;
75        let blob_code_id = deployment.blob.code_id()?;
76
77        let creator_account_id: cosmrs::AccountId = admin.as_str().parse().unwrap();
78        let canon_creator = CanonicalAddr::from(creator_account_id.to_bytes());
79
80        let expected_addr = |salt: &[u8]| -> Result<CanonicalAddr, Instantiate2AddressError> {
81            instantiate2_address(&cw_blob::CHECKSUM, &canon_creator, salt)
82        };
83
84        deployment.ans_host.deterministic_instantiate(
85            &abstract_std::ans_host::MigrateMsg::Instantiate(
86                abstract_std::ans_host::InstantiateMsg {
87                    admin: admin.to_string(),
88                },
89            ),
90            blob_code_id,
91            expected_addr(native_addrs::ANS_HOST_SALT)?,
92            Binary::from(native_addrs::ANS_HOST_SALT),
93        )?;
94
95        deployment.registry.deterministic_instantiate(
96            &abstract_std::registry::MigrateMsg::Instantiate(
97                abstract_std::registry::InstantiateMsg {
98                    admin: admin.to_string(),
99                    #[cfg(feature = "testing")]
100                    security_enabled: Some(false),
101                    #[cfg(not(feature = "testing"))]
102                    security_enabled: Some(true),
103                    namespace_registration_fee: None,
104                },
105            ),
106            blob_code_id,
107            expected_addr(native_addrs::REGISTRY_SALT)?,
108            Binary::from(native_addrs::REGISTRY_SALT),
109        )?;
110        deployment.module_factory.deterministic_instantiate(
111            &abstract_std::module_factory::MigrateMsg::Instantiate(
112                abstract_std::module_factory::InstantiateMsg {
113                    admin: admin.to_string(),
114                },
115            ),
116            blob_code_id,
117            expected_addr(native_addrs::MODULE_FACTORY_SALT)?,
118            Binary::from(native_addrs::MODULE_FACTORY_SALT),
119        )?;
120
121        // We also instantiate ibc contracts
122        deployment
123            .ibc
124            .instantiate(&Addr::unchecked(admin.clone()))?;
125
126        deployment.registry.register_base(&deployment.account)?;
127        deployment
128            .registry
129            .register_natives(deployment.contracts())?;
130        deployment.registry.approve_any_abstract_modules()?;
131
132        // Create the first abstract account
133        AccountI::create_default_account(
134            &deployment,
135            GovernanceDetails::Monarchy {
136                monarch: chain.sender_addr().to_string(),
137            },
138        )?;
139
140        Ok(deployment)
141    }
142
143    fn get_contracts_mut(&mut self) -> Vec<Box<&mut dyn ContractInstance<Chain>>> {
144        vec![
145            Box::new(&mut self.ans_host),
146            Box::new(&mut self.registry),
147            Box::new(&mut self.module_factory),
148            Box::new(&mut self.account),
149            Box::new(&mut self.ibc.client),
150            Box::new(&mut self.ibc.host),
151        ]
152    }
153
154    fn load_from(chain: Chain) -> Result<Self, Self::Error> {
155        #[allow(unused_mut)]
156        let mut abstr = Self::new(chain.clone());
157        #[cfg(feature = "daemon")]
158        {
159            // We register all the contracts default state
160            let state = crate::AbstractDaemonState::default().state();
161
162            abstr.set_contracts_state(Some(state));
163        }
164        // Check if abstract deployed, for successful load
165        if let Err(CwOrchError::AddrNotInStore(_)) = abstr.registry.address() {
166            return Err(AbstractInterfaceError::NotDeployed(
167                chain
168                    .block_info()
169                    .map(|b| b.chain_id)
170                    .unwrap_or("chain id not available".to_string()),
171            ));
172        } else if abstr.registry.item_query(cw2::CONTRACT).is_err() {
173            return Err(AbstractInterfaceError::NotDeployed(
174                chain
175                    .block_info()
176                    .map(|b| b.chain_id)
177                    .unwrap_or("chain id not available".to_string()),
178            ));
179        }
180        Ok(abstr)
181    }
182}
183
184#[cfg(feature = "daemon")]
185impl<Chain: CwEnv> DeployedChains<Chain> for Abstract<Chain> {
186    fn deployed_state_file_path() -> Option<String> {
187        let crate_path = env!("CARGO_MANIFEST_DIR");
188
189        Some(
190            std::path::PathBuf::from(crate_path)
191                .join("state.json")
192                .display()
193                .to_string(),
194        )
195    }
196}
197
198impl<Chain: CwEnv> Abstract<Chain> {
199    pub fn new(chain: Chain) -> Self {
200        let (ans_host, registry, module_factory) = get_native_contracts(chain.clone());
201        let (ibc_client, ibc_host) = get_ibc_contracts(chain.clone());
202        let account = AccountI::new(ACCOUNT, chain.clone());
203        Self {
204            account,
205            ans_host,
206            registry,
207            module_factory,
208            ibc: AbstractIbc {
209                client: ibc_client,
210                host: ibc_host,
211            },
212            blob: CwBlob::new(CW_BLOB, chain),
213        }
214    }
215
216    pub fn contracts(&self) -> Vec<(&cw_orch::contract::Contract<Chain>, String)> {
217        vec![
218            (
219                self.ans_host.as_instance(),
220                ans_host::contract::CONTRACT_VERSION.to_string(),
221            ),
222            (
223                self.registry.as_instance(),
224                registry::contract::CONTRACT_VERSION.to_string(),
225            ),
226            (
227                self.module_factory.as_instance(),
228                module_factory::contract::CONTRACT_VERSION.to_string(),
229            ),
230            (
231                self.ibc.client.as_instance(),
232                ibc_client::contract::CONTRACT_VERSION.to_string(),
233            ),
234            (
235                self.ibc.host.as_instance(),
236                ibc_host::contract::CONTRACT_VERSION.to_string(),
237            ),
238        ]
239    }
240
241    pub fn call_as(&self, sender: &<Chain as TxHandler>::Sender) -> Self {
242        Self {
243            ans_host: self.ans_host.clone().call_as(sender),
244            registry: self.registry.clone().call_as(sender),
245            module_factory: self.module_factory.clone().call_as(sender),
246            ibc: self.ibc.call_as(sender),
247            account: self.account.call_as(sender),
248            blob: self.blob.clone(),
249        }
250    }
251
252    pub fn account_code_id(&self) -> Result<u64, AbstractInterfaceError> {
253        let account_module_info = &self
254            .registry
255            .modules(vec![ModuleInfo::from_id_latest(ACCOUNT)?])?
256            .modules[0];
257
258        match account_module_info.module.reference {
259            abstract_std::objects::module_reference::ModuleReference::Account(code_id) => Ok(code_id),
260            _ => panic!("Your abstract instance has an account module that is not registered as an account. This is bad"),
261        }
262    }
263}