Skip to content

Add multisig support to abstract deployment #532

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
499 changes: 382 additions & 117 deletions framework/Cargo.lock

Large diffs are not rendered by default.

18 changes: 17 additions & 1 deletion framework/packages/abstract-interface/Cargo.toml
Original file line number Diff line number Diff line change
@@ -22,6 +22,13 @@ default = ["integration"]
daemon = ["cw-orch/daemon"]
integration = []
interchain = ["dep:cw-orch-interchain", "dep:cw-orch-polytone"]
multisig = [
"dep:cw-plus-orch",
"dep:cw-utils",
"dep:cw3",
"dep:cw4",
"dep:cw-ownable",
]

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
cosmwasm-std = { workspace = true }
@@ -59,7 +66,16 @@ workspace-hack = { version = "0.1", path = "../../workspace-hack" }

# Predictable abstract addresses
cw-blob = { workspace = true }
cosmrs = { version = "0.19.0" }
cosmrs = { version = "0.20.0", features = ["cosmwasm"] }
prost-types = { version = "0.13.3" }
prost = { version = "0.13.3" }

# Multisig
cw-plus-orch = { version = "0.25.0", optional = true }
cw-utils = { version = "2.0.0", optional = true }
cw3 = { version = "2.0.0", optional = true }
cw4 = { version = "2.0.0", optional = true }
cw-ownable = { workspace = true, optional = true }

[build-dependencies]
serde_json = "1.0.79"
12 changes: 12 additions & 0 deletions framework/packages/abstract-interface/src/deployment.rs
Original file line number Diff line number Diff line change
@@ -5,6 +5,8 @@ use cw_orch::daemon::DeployedChains;

use cw_orch::{mock::MockBase, prelude::*};

#[cfg(feature = "multisig")]
use crate::multisig::AbstractMultisig;
use crate::{
get_ibc_contracts, get_native_contracts, AbstractIbc, AbstractInterfaceError, AccountI,
AnsHost, ModuleFactory, Registry,
@@ -21,6 +23,8 @@ pub struct Abstract<Chain: CwEnv> {
pub ibc: AbstractIbc<Chain>,
pub(crate) account: AccountI<Chain>,
pub(crate) blob: CwBlob<Chain>,
#[cfg(feature = "multisig")]
pub multisig: AbstractMultisig<Chain>,
}

impl<Chain: CwEnv> Deploy<Chain> for Abstract<Chain> {
@@ -52,6 +56,8 @@ impl<Chain: CwEnv> Deploy<Chain> for Abstract<Chain> {
account,
ibc: ibc_infra,
blob,
#[cfg(feature = "multisig")]
multisig: AbstractMultisig::new(&chain),
};

Ok(deployment)
@@ -211,6 +217,8 @@ impl<Chain: CwEnv> Abstract<Chain> {
client: ibc_client,
host: ibc_host,
},
#[cfg(feature = "multisig")]
multisig: AbstractMultisig::new(&chain),
blob: CwBlob::new(CW_BLOB, chain),
}
}
@@ -287,6 +295,8 @@ impl<Chain: CwEnv> Abstract<Chain> {
ibc,
account,
blob: _,
#[cfg(feature = "multisig")]
multisig: _,
} = self;
ans_host.set_sender(sender);
registry.set_sender(sender);
@@ -304,6 +314,8 @@ impl<Chain: CwEnv> Abstract<Chain> {
ibc: self.ibc.call_as(sender),
account: self.account.call_as(sender),
blob: self.blob.clone(),
#[cfg(feature = "multisig")]
multisig: self.multisig.clone(),
}
}
}
3 changes: 3 additions & 0 deletions framework/packages/abstract-interface/src/error.rs
Original file line number Diff line number Diff line change
@@ -29,6 +29,9 @@ pub enum AbstractInterfaceError {

#[error("No matching module deployed {0:?}")]
NoMatchingModule(StaticDependency),

#[error("Multisig error: {0}")]
Multisig(String),
}

impl AbstractInterfaceError {
3 changes: 3 additions & 0 deletions framework/packages/abstract-interface/src/lib.rs
Original file line number Diff line number Diff line change
@@ -28,3 +28,6 @@ pub use error::AbstractInterfaceError;
pub use crate::{deployers::*, deployment::*};

pub use daemon_state::AbstractDaemonState;

#[cfg(feature = "multisig")]
pub mod multisig;
2 changes: 1 addition & 1 deletion framework/packages/abstract-interface/src/migrate.rs
Original file line number Diff line number Diff line change
@@ -200,7 +200,7 @@ impl<T: CwEnv> Abstract<T> {
}
}

fn contract_version<Chain: CwEnv, A: ContractInstance<Chain>>(
pub(crate) fn contract_version<Chain: CwEnv, A: ContractInstance<Chain>>(
contract: &A,
) -> Result<ContractVersion, crate::AbstractInterfaceError> {
let wasm_querier = contract.environment().wasm_querier();
449 changes: 449 additions & 0 deletions framework/packages/abstract-interface/src/multisig.rs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -321,7 +321,7 @@ impl<Chain: CwEnv> Registry<Chain> {
Ok(())
}

fn contracts_into_module_entries<RefFn>(
pub(crate) fn contracts_into_module_entries<RefFn>(
&self,
modules: Vec<(&Contract<Chain>, VersionString)>,
ref_fn: RefFn,
24 changes: 14 additions & 10 deletions framework/workspace-hack/Cargo.toml
Original file line number Diff line number Diff line change
@@ -85,8 +85,9 @@ byteorder = { version = "1", features = ["i128"] }
bytes = { version = "1" }
chrono = { version = "0.4", features = ["serde"] }
console = { version = "0.15" }
cosmos-sdk-proto = { version = "0.24", default-features = false, features = ["cosmwasm", "grpc-transport"] }
cosmrs = { version = "0.19", features = ["cosmwasm", "dev", "grpc"] }
cosmos-sdk-proto-2ffb4c3fe830441c = { package = "cosmos-sdk-proto", version = "0.25", default-features = false, features = ["cosmwasm", "std"] }
cosmos-sdk-proto-adf3d7031871b0af = { package = "cosmos-sdk-proto", version = "0.24", default-features = false, features = ["cosmwasm", "grpc-transport"] }
cosmrs = { version = "0.20", features = ["cosmwasm"] }
crossbeam-epoch = { version = "0.9" }
crunchy = { version = "0.2", features = ["std"] }
cw-orch-interchain-core = { version = "0.9", default-features = false, features = ["daemon"] }
@@ -161,8 +162,9 @@ byteorder = { version = "1", features = ["i128"] }
bytes = { version = "1" }
chrono = { version = "0.4", features = ["serde"] }
console = { version = "0.15" }
cosmos-sdk-proto = { version = "0.24", default-features = false, features = ["cosmwasm", "grpc-transport"] }
cosmrs = { version = "0.19", features = ["cosmwasm", "dev", "grpc"] }
cosmos-sdk-proto-2ffb4c3fe830441c = { package = "cosmos-sdk-proto", version = "0.25", default-features = false, features = ["cosmwasm", "std"] }
cosmos-sdk-proto-adf3d7031871b0af = { package = "cosmos-sdk-proto", version = "0.24", default-features = false, features = ["cosmwasm", "grpc-transport"] }
cosmrs = { version = "0.20", features = ["cosmwasm"] }
crossbeam-epoch = { version = "0.9" }
crunchy = { version = "0.2", features = ["std"] }
cw-orch-interchain-core = { version = "0.9", default-features = false, features = ["daemon"] }
@@ -214,7 +216,7 @@ serde_json = { version = "1", features = ["alloc", "raw_value"] }
signature = { version = "2", default-features = false, features = ["digest", "rand_core", "std"] }
smallvec = { version = "1", default-features = false, features = ["const_new"] }
spki = { version = "0.7", default-features = false, features = ["pem", "std"] }
syn-f595c2ba2a3f28df = { package = "syn", version = "2", default-features = false, features = ["visit"] }
syn-f595c2ba2a3f28df = { package = "syn", version = "2", default-features = false, features = ["fold", "visit"] }
tendermint = { version = "0.38", features = ["secp256k1"] }
time = { version = "0.3", features = ["macros", "parsing"] }
tiny-keccak = { version = "2", features = ["keccak"] }
@@ -238,8 +240,9 @@ byteorder = { version = "1", features = ["i128"] }
bytes = { version = "1" }
chrono = { version = "0.4", features = ["serde"] }
console = { version = "0.15" }
cosmos-sdk-proto = { version = "0.24", default-features = false, features = ["cosmwasm", "grpc-transport"] }
cosmrs = { version = "0.19", features = ["cosmwasm", "dev", "grpc"] }
cosmos-sdk-proto-2ffb4c3fe830441c = { package = "cosmos-sdk-proto", version = "0.25", default-features = false, features = ["cosmwasm", "std"] }
cosmos-sdk-proto-adf3d7031871b0af = { package = "cosmos-sdk-proto", version = "0.24", default-features = false, features = ["cosmwasm", "grpc-transport"] }
cosmrs = { version = "0.20", features = ["cosmwasm"] }
crossbeam-epoch = { version = "0.9" }
crunchy = { version = "0.2", features = ["std"] }
cw-orch-interchain-core = { version = "0.9", default-features = false, features = ["daemon"] }
@@ -313,8 +316,9 @@ byteorder = { version = "1", features = ["i128"] }
bytes = { version = "1" }
chrono = { version = "0.4", features = ["serde"] }
console = { version = "0.15" }
cosmos-sdk-proto = { version = "0.24", default-features = false, features = ["cosmwasm", "grpc-transport"] }
cosmrs = { version = "0.19", features = ["cosmwasm", "dev", "grpc"] }
cosmos-sdk-proto-2ffb4c3fe830441c = { package = "cosmos-sdk-proto", version = "0.25", default-features = false, features = ["cosmwasm", "std"] }
cosmos-sdk-proto-adf3d7031871b0af = { package = "cosmos-sdk-proto", version = "0.24", default-features = false, features = ["cosmwasm", "grpc-transport"] }
cosmrs = { version = "0.20", features = ["cosmwasm"] }
crossbeam-epoch = { version = "0.9" }
crunchy = { version = "0.2", features = ["std"] }
cw-orch-interchain-core = { version = "0.9", default-features = false, features = ["daemon"] }
@@ -366,7 +370,7 @@ serde_json = { version = "1", features = ["alloc", "raw_value"] }
signature = { version = "2", default-features = false, features = ["digest", "rand_core", "std"] }
smallvec = { version = "1", default-features = false, features = ["const_new"] }
spki = { version = "0.7", default-features = false, features = ["pem", "std"] }
syn-f595c2ba2a3f28df = { package = "syn", version = "2", default-features = false, features = ["visit"] }
syn-f595c2ba2a3f28df = { package = "syn", version = "2", default-features = false, features = ["fold", "visit"] }
tendermint = { version = "0.38", features = ["secp256k1"] }
time = { version = "0.3", features = ["macros", "parsing"] }
tiny-keccak = { version = "2", features = ["keccak"] }
1 change: 1 addition & 0 deletions interchain/Cargo.toml
Original file line number Diff line number Diff line change
@@ -62,6 +62,7 @@ cw-orch-daemon = { version = "0.28.0" }
cw-orch-interchain = { version = "0.8.0" }
cw-orch-clone-testing = { version = "0.9.0" }
cw-orch-proto = { version = "0.9.0" }
cw-plus-orch = { version = "0.25.0" }

# Keep these as path, creates cirular dependency otherwise
# Only need to re-publish all contracts if a re-publish of abstract-interface is required
4 changes: 3 additions & 1 deletion interchain/scripts/Cargo.toml
Original file line number Diff line number Diff line change
@@ -19,9 +19,10 @@ cw-orch = { workspace = true, features = ["daemon"] }
cw-orch-interchain = { workspace = true, features = ["daemon"] }
cw-orch-clone-testing = { workspace = true }
cw-orch-polytone = { workspace = true }
cw-plus-orch = { workspace = true }
abstract-std = { workspace = true }
abstract-xion = { workspace = true }
abstract-interface = { workspace = true, features = ["daemon"] }
abstract-interface = { workspace = true, features = ["daemon", "multisig"] }
tokio = { workspace = true, features = ["full"] }
log = "0.4.14"
anyhow = { workspace = true }
@@ -64,4 +65,5 @@ prost = { version = "0.13" }
prost-types = { version = "0.13" }

cw-orch-core = "2.0.0"
cw4 = "2.0.0"
cosmwasm-schema.workspace = true
81 changes: 81 additions & 0 deletions interchain/scripts/src/bin/multisig_proposal_template.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//! Template to create proposal as abstract multisig
use abstract_interface::Abstract;
use clap::Parser;
use cw_orch::prelude::{
networks::{parse_network, ChainInfo},
*,
};
use cw_plus_orch::cw3_flex_multisig::ExecuteMsgInterfaceFns;
use tokio::runtime::Runtime;

pub const ABSTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");

#[allow(unused)]
fn migrate(networks: Vec<ChainInfo>) -> anyhow::Result<()> {
let rt = Runtime::new()?;
for network in networks {
let chain = DaemonBuilder::new(network).handle(rt.handle()).build()?;

let deployment = Abstract::load_from(chain.clone())?;

let mut msgs = vec![];

// Example of abstract action
msgs.extend(deployment.multisig.propose_on_ans_msgs(
&deployment.ans_host,
vec![abstract_std::ans_host::ExecuteMsg::UpdateAssetAddresses {
to_add: vec![],
to_remove: vec![],
}],
)?);

msgs.push(todo!());

let title: &str = todo!();
let description: &str = todo!();
let latest = None;
deployment
.multisig
.cw3
.propose(description, msgs, title, latest, &[])?;
}

Ok(())
}

#[derive(Parser, Default, Debug)]
#[command(author, version, about, long_about = None)]
struct Arguments {
/// Network Id to deploy on
#[arg(short, long, value_delimiter = ' ', num_args = 1..)]
network_ids: Vec<String>,
}

fn main() {
dotenv().ok();
env_logger::init();
use dotenv::dotenv;
let args = Arguments::parse();

let networks = args
.network_ids
.iter()
.map(|n| parse_network(n).unwrap())
.collect::<Vec<_>>();

if let Err(ref err) = migrate(networks) {
log::error!("{}", err);
err.chain()
.skip(1)
.for_each(|cause| log::error!("because: {}", cause));

// The backtrace is not always generated. Try to run this example
// with `$env:RUST_BACKTRACE=1`.
// if let Some(backtrace) = e.backtrace() {
// log::debug!("backtrace: {:?}", backtrace);
// }

::std::process::exit(1);
}
}
60 changes: 60 additions & 0 deletions interchain/scripts/src/bin/multisig_propose_migrate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//! This script creates proposal to migrate contracts and register them in the registry
use abstract_interface::Abstract;
use clap::Parser;
use cw_orch::prelude::{
networks::{parse_network, ChainInfo},
*,
};
use tokio::runtime::Runtime;

pub const ABSTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");

fn migrate(networks: Vec<ChainInfo>) -> anyhow::Result<()> {
let rt = Runtime::new()?;
for network in networks {
let chain = DaemonBuilder::new(network).handle(rt.handle()).build()?;

let deployment = Abstract::load_from(chain.clone())?;

deployment.propose_migrate_if_version_changed()?;
}

Ok(())
}

#[derive(Parser, Default, Debug)]
#[command(author, version, about, long_about = None)]
struct Arguments {
/// Network Id to deploy on
#[arg(short, long, value_delimiter = ' ', num_args = 1..)]
network_ids: Vec<String>,
}

fn main() {
dotenv().ok();
env_logger::init();
use dotenv::dotenv;
let args = Arguments::parse();

let networks = args
.network_ids
.iter()
.map(|n| parse_network(n).unwrap())
.collect::<Vec<_>>();

if let Err(ref err) = migrate(networks) {
log::error!("{}", err);
err.chain()
.skip(1)
.for_each(|cause| log::error!("because: {}", cause));

// The backtrace is not always generated. Try to run this example
// with `$env:RUST_BACKTRACE=1`.
// if let Some(backtrace) = e.backtrace() {
// log::debug!("backtrace: {:?}", backtrace);
// }

::std::process::exit(1);
}
}
40 changes: 40 additions & 0 deletions interchain/scripts/src/bin/update_to_multisig.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//! This script moves ownership of the abstract to the newly-created multisig
//! You need to fill members and network to run it correctly
use std::sync::Arc;

use abstract_interface::Abstract;
use cw_orch::prelude::*;
use cw_orch_daemon::{CosmosOptions, Wallet};
use networks::LOCAL_JUNO;

fn main() -> anyhow::Result<()> {
dotenv().ok();
env_logger::init();
use dotenv::dotenv;

let abstract_mnemonic =
std::env::var("MULTISIG_MNEMONIC").expect("Fill your abstract mnemonic");
// Fill members
let members = vec![];
assert!(!members.is_empty(), "Fill multisig members first");

// Change network
let network = LOCAL_JUNO;

let chain = DaemonBuilder::new(network).build()?;
let proposal_creator = chain.rt_handle.block_on(Wallet::new(
&Arc::new(chain.chain_info().clone()),
CosmosOptions::default().mnemonic(abstract_mnemonic),
))?;

let mut deployment = Abstract::load_from(chain.clone())?;
deployment.update_admin_to_multisig(
chain.sender_addr().to_string(),
members,
&proposal_creator,
[],
)?;

Ok(())
}