Skip to content

Commit

Permalink
Fix install modules on subaccount (#503)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kayanski authored Oct 25, 2024
1 parent 8a369b2 commit 900e493
Show file tree
Hide file tree
Showing 15 changed files with 383 additions and 469 deletions.
288 changes: 61 additions & 227 deletions framework/packages/abstract-client/src/account.rs

Large diffs are not rendered by default.

55 changes: 47 additions & 8 deletions framework/packages/abstract-client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ use rand::Rng;
use crate::{
account::{Account, AccountBuilder},
source::AccountSource,
AbstractClientError, Environment, PublisherBuilder, Service,
AbstractClientError, Environment, Publisher, Service,
};

/// Client to interact with Abstract accounts and modules
Expand Down Expand Up @@ -152,17 +152,56 @@ impl<Chain: CwEnv> AbstractClient<Chain> {
.map_err(|e| AbstractClientError::CwOrch(e.into()))
}

/// Publisher builder for creating new [`Publisher`](crate::Publisher) Abstract Account
/// To publish any modules your account requires to have claimed a namespace.
pub fn publisher_builder(&self, namespace: Namespace) -> PublisherBuilder<Chain> {
PublisherBuilder::new(AccountBuilder::new(&self.abstr), namespace)
/// Fetches an existing Abstract [`Publisher`] from chain
pub fn fetch_publisher(&self, namespace: Namespace) -> AbstractClientResult<Publisher<Chain>> {
let account = self.fetch_account(namespace)?;
account.publisher()
}

/// Builder for creating a new Abstract [`Account`].
pub fn account_builder(&self) -> AccountBuilder<Chain> {
AccountBuilder::new(&self.abstr)
}

/// Builder for creating a new Abstract [`Account`].
pub fn sub_account_builder<'a>(
&'a self,
account: &'a Account<Chain>,
) -> AbstractClientResult<AccountBuilder<Chain>> {
let mut builder = AccountBuilder::new(&self.abstr);
builder.sub_account(account);
builder.name("Sub Account");
Ok(builder)
}

/// Fetches an existing Abstract [`Account`] from chain
pub fn fetch_account(&self, namespace: Namespace) -> AbstractClientResult<Account<Chain>> {
Account::maybe_from_namespace(&self.abstr, namespace.clone())?.ok_or(
AbstractClientError::NamespaceNotClaimed {
namespace: namespace.to_string(),
},
)
}

/// Fetches an existing Abstract [`Account`] from chain
/// If the Namespace is not claimed, creates an account with the provided account builder
pub fn fetch_or_build_account(
&self,
namespace: Namespace,
build_fn: for<'a, 'b> fn(
&'a mut AccountBuilder<'b, Chain>,
) -> &'a mut AccountBuilder<'b, Chain>,
) -> AbstractClientResult<Account<Chain>> {
match Account::maybe_from_namespace(&self.abstr, namespace.clone())? {
Some(account) => Ok(account),
None => {
let mut account_builder = self.account_builder();
account_builder.namespace(namespace);
build_fn(&mut account_builder).build()
}
}
}

/// Address of the sender
pub fn sender(&self) -> Addr {
self.environment().sender_addr()
Expand All @@ -188,7 +227,7 @@ impl<Chain: CwEnv> AbstractClient<Chain> {
// if namespace, check if we need to claim or not.
// Check if namespace already claimed
let account_from_namespace_result: Option<Account<Chain>> =
Account::maybe_from_namespace(&self.abstr, namespace.clone(), true)?;
Account::maybe_from_namespace(&self.abstr, namespace.clone())?;

// Only return if the account can be retrieved without errors.
if let Some(account_from_namespace) = account_from_namespace_result {
Expand All @@ -201,7 +240,7 @@ impl<Chain: CwEnv> AbstractClient<Chain> {
}
AccountSource::AccountId(account_id) => {
let abstract_account = AccountI::load_from(&self.abstr, account_id.clone())?;
Ok(Account::new(abstract_account, true))
Ok(Account::new(abstract_account))
}
AccountSource::App(app) => {
// Query app for account address and get AccountId from it.
Expand All @@ -222,7 +261,7 @@ impl<Chain: CwEnv> AbstractClient<Chain> {
.map_err(Into::into)?;
// This function verifies the account-id is valid and returns an error if not.
let abstract_account = AccountI::load_from(&self.abstr, account_config.account_id)?;
Ok(Account::new(abstract_account, true))
Ok(Account::new(abstract_account))
}
}
}
Expand Down
14 changes: 13 additions & 1 deletion framework/packages/abstract-client/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
//! # Represents Abstract Client Errors

use abstract_interface::AbstractInterfaceError;
use abstract_std::{objects::validation::ValidationError, AbstractError};
use abstract_std::{
objects::{validation::ValidationError, AccountId},
AbstractError,
};
use thiserror::Error;

#[derive(Error, Debug)]
Expand Down Expand Up @@ -29,6 +32,15 @@ pub enum AbstractClientError {
#[error("Can't retrieve Account for unclaimed namespace \"{namespace}\".")]
NamespaceNotClaimed { namespace: String },

#[error("Namespace \"{namespace}\" already claimed by account {account_id}")]
NamespaceClaimed {
namespace: String,
account_id: AccountId,
},

#[error("Account {account} doesn't have an associated namespace")]
NoNamespace { account: AccountId },

#[error("Can't add custom funds when using auto_fund.")]
FundsWithAutoFund {},

Expand Down
2 changes: 1 addition & 1 deletion framework/packages/abstract-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub use builder::AbstractClientBuilder;
pub use client::AbstractClient;
pub use error::AbstractClientError;
pub use infrastructure::Environment;
pub use publisher::{Publisher, PublisherBuilder};
pub use publisher::Publisher;
pub use service::Service;
pub use source::AccountSource;

Expand Down
136 changes: 49 additions & 87 deletions framework/packages/abstract-client/src/publisher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,106 +3,68 @@
//! [`Publisher`] is an Account with helpers for publishing and maintaining Abstract Applications and Adapters

use abstract_interface::{
AdapterDeployer, AppDeployer, DeployStrategy, RegisteredModule, ServiceDeployer,
StandaloneDeployer,
AdapterDeployer, AppDeployer, DeployStrategy, RegisteredModule, RegistryQueryFns,
ServiceDeployer, StandaloneDeployer,
};
use abstract_std::objects::{gov_type::GovernanceDetails, namespace::Namespace};
use cw_orch::{contract::Contract, prelude::*};
use serde::Serialize;

use crate::{
account::{Account, AccountBuilder},
client::AbstractClientResult,
Environment,
account::Account, client::AbstractClientResult, infrastructure::Infrastructure,
AbstractClientError, Environment,
};

/// A builder for creating [`Publishers`](Account).
/// Get the builder from the [`AbstractClient::publisher_builder`](crate::AbstractClient)
/// and create the account with the `build` method.
///
/// ```
/// # use abstract_client::{AbstractClientError, Environment};
/// # use cw_orch::prelude::*;
/// # let chain = MockBech32::new("mock");
/// # let abstr_client = abstract_client::AbstractClient::builder(chain.clone()).build_mock().unwrap();
/// # let chain = abstr_client.environment();
/// use abstract_client::{AbstractClient, Publisher, Namespace};
///
/// let client = AbstractClient::new(chain)?;
///
/// let namespace = Namespace::new("alice-namespace")?;
/// let publisher: Publisher<MockBech32> = client.publisher_builder(namespace)
/// .name("alice")
/// // other configurations
/// .build()?;
/// # Ok::<(), AbstractClientError>(())
/// ```
pub struct PublisherBuilder<'a, Chain: CwEnv> {
account_builder: AccountBuilder<'a, Chain>,
}

impl<'a, Chain: CwEnv> PublisherBuilder<'a, Chain> {
pub(crate) fn new(
mut account_builder: AccountBuilder<'a, Chain>,
namespace: Namespace,
) -> Self {
account_builder.namespace(namespace);
Self { account_builder }
}

/// Username for the account
/// Defaults to "Default Abstract Account"
pub fn name(&mut self, name: impl Into<String>) -> &mut Self {
self.account_builder.name(name);
self
}

/// Description for the account
pub fn description(&mut self, description: impl Into<String>) -> &mut Self {
self.account_builder.description(description);
self
}

/// http(s) or ipfs link for the account
pub fn link(&mut self, link: impl Into<String>) -> &mut Self {
self.account_builder.link(link);
self
}

/// Overwrite the configured namespace
pub fn namespace(&mut self, namespace: Namespace) -> &mut Self {
self.account_builder.namespace(namespace);
self
}

/// Governance of the account.
/// Defaults to the [`GovernanceDetails::Monarchy`] variant, owned by the sender
pub fn ownership(&mut self, ownership: GovernanceDetails<String>) -> &mut Self {
self.account_builder.ownership(ownership);
self
}

/// Install modules on a new sub-account instead of current account.
/// Defaults to `true`
pub fn install_on_sub_account(&mut self, value: bool) -> &mut Self {
self.account_builder.install_on_sub_account(value);
self
}

/// Builds the [`Publisher`].
/// Creates an account if the namespace is not already owned.
pub fn build(&self) -> AbstractClientResult<Publisher<Chain>> {
let account = self.account_builder.build()?;
Ok(Publisher { account })
}
}
use abstract_std::registry::NamespaceResponse;

/// A Publisher represents an account that owns a namespace with the goal of publishing modules to the on-chain module-store.
pub struct Publisher<Chain: CwEnv> {
account: Account<Chain>,
}

impl<Chain: CwEnv> Publisher<Chain> {
/// New publisher from account. We check that the account has an associated namespace.
/// If you create a publisher from an account without a namespace, use [`Self::new_with_namespace`] to claim it
pub fn new(account: &Account<Chain>) -> AbstractClientResult<Self> {
let namespace = account
.infrastructure()?
.registry
.namespaces(vec![account.id()?])?;

if namespace.namespaces.is_empty() {
return Err(AbstractClientError::NoNamespace {
account: account.id()?,
});
}

Ok(Self {
account: account.clone(),
})
}

/// New publisher from account and namespace
/// Claim the namespace from the account
pub fn new_with_namespace(
account: Account<Chain>,
namespace: &str,
) -> AbstractClientResult<Self> {
let namespace_response = account
.infrastructure()?
.registry
.namespace(namespace.try_into()?)?;

if let NamespaceResponse::Claimed(namespace_info) = namespace_response {
if namespace_info.account_id != account.id()? {
return Err(AbstractClientError::NamespaceClaimed {
namespace: namespace.to_string(),
account_id: namespace_info.account_id,
});
}
} else {
account.claim_namespace(namespace)?;
}

Ok(Self { account })
}

/// Publish an Abstract App
pub fn publish_app<
M: ContractInstance<Chain> + RegisteredModule + From<Contract<Chain>> + AppDeployer<Chain>,
Expand Down
Loading

0 comments on commit 900e493

Please sign in to comment.