Skip to content

Commit

Permalink
Merge pull request #68 from AbstractSDK/example-workspace-2
Browse files Browse the repository at this point in the history
Update to workspace, include example adapter and package
  • Loading branch information
CyberHoward authored May 14, 2024
2 parents 5a6b724 + 6ee821b commit 9f8edf7
Show file tree
Hide file tree
Showing 43 changed files with 1,114 additions and 325 deletions.
8 changes: 5 additions & 3 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
[alias]
wasm = "build --release --target wasm32-unknown-unknown"
unit-test = "test --lib"
schema = "run --example schema --features schema"

publish = "run --example publish --features daemon"
local_daemon = "run --example local_daemon --features daemon"
# In case your project have multiple contracts
# you may end up having multiple packages with those example binaries.
# You can specify your package with `--package` option
schema = "run --example schema --features schema"
publish_module = "run --example publish --features daemon"
73 changes: 25 additions & 48 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,63 +1,40 @@
[package]
name = "app"
version = "0.0.1"
authors = [
"CyberHoward <[email protected]>",
"Adair <[email protected]>",
"Abstract Money <[email protected]>",
]
edition = "2021"
homepage = ""
documentation = ""
repository = ""
license = "GPL-3.0-or-later"
keywords = ["cosmos", "cosmwasm", "abstractsdk"]
[workspace]
members = ["contracts/*"]
resolver = "2"

exclude = ["contract.wasm", "hash.txt"]

[lib]
crate-type = ["cdylib", "rlib"]

[[example]]
name = "schema"
required-features = ["schema"]

[[example]]
name = "local_daemon"
required-features = ["daemon"]

[[example]]
name = "publish"
required-features = ["daemon"]

[features]
default = ["export"]
export = []
schema = ["abstract-app/schema"]
interface = ["export", "abstract-app/interface-macro", "dep:cw-orch"]
daemon = ["interface", "cw-orch/daemon"]

[dependencies]
[profile.release]
rpath = false
lto = true
overflow-checks = true
opt-level = 3
debug = false
debug-assertions = false
codegen-units = 1
panic = 'abort'
incremental = false

[workspace.dependencies]
cosmwasm-std = { version = "1.5.3" }
cosmwasm-schema = { version = "1.5.3" }
cw-controllers = { version = "1.1.2" }
cw-storage-plus = "1.2.0"
thiserror = { version = "1.0.50" }
schemars = "0.8"
cw-asset = { version = "3.0.0" }
abstract-app = { version = "0.21.0" }
abstract-app = { version = "0.22.2" }
abstract-adapter = { version = "0.22.2" }
abstract-interface = { version = "0.22.0" }
abstract-client = { version = "0.22.0" }
lazy_static = "1.4.0"


# Dependencies for interface
cw-orch = { version = "0.20.1", optional = true }
cw-orch = { version = "0.22.2" }
const_format = "0.2.32"

[dev-dependencies]
app = { path = ".", features = ["interface"] }
abstract-client = { version = "0.21.0" }
abstract-app = { version = "0.21", features = ["test-utils"] }
my-app = { path = "contracts/app" }
my-adapter = { path = "contracts/adapter" }
speculoos = "0.11.0"
semver = "1.0"
dotenv = "0.15.0"
env_logger = "0.10.0"
cw-orch = { version = "0.20.1" }
clap = { version = "4.3.7", features = ["derive"] }
clap = { version = "4.3.7" }
22 changes: 19 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Abstract App Module Template

The Abstract App Module Template is a starting point for developing composable smart-contracts, or "Apps" on the Abstract platform. An App is instantiated for each Account individually and is migratable. Apps are allowed to perform actions on Abstract Accounts and can integrate with other Apps and Adapters installed on the Account. To learn more about Abstract Accounts, please see the [abstract accounts documentation](https://docs.abstract.money/3_framework/3_architecture.html). To read more about apps, please see the [app module documentation](https://docs.abstract.money/3_framework/6_module_types.html).
The Abstract Module Template is a starting point for developing composable smart-contracts, or "Apps" on the Abstract platform. An App is instantiated for each Account individually and is migratable. Apps are allowed to perform actions on Abstract Accounts and can integrate with other Apps and Adapters installed on the Account. To learn more about Abstract Accounts, please see the [abstract accounts documentation](https://docs.abstract.money/3_framework/3_architecture.html). To read more about apps, please see the [app module documentation](https://docs.abstract.money/3_framework/6_module_types.html).

This template includes examples for both an app and adapter in a workspace. It also creates a separate package for all apps / adapters developed under your Abstract namespace for an easy monorepo setup.

## Getting Started

Expand All @@ -19,6 +21,20 @@ chmod +x ./template-setup.sh

The setup will suggest you to install a few tools that are used in the template. You can skip this step if you already have them installed or if you're not planning on using them.

### Updating Names

We recommend updating the names of the packages and variables to be custom to your application. You should be able to do this easily with a global replace.

For example:
- ~~my-package~~ -> ibcmail
- ~~my-app~~ -> ibcmail-client
- ~~my_app~~ -> ibcmail_client
- ~~MyApp~~ -> IbcMailClient
- ~~my-adapter~~ -> ibcmail-server
- ~~my_adapter~~ -> ibcmail_server
- ~~MyApp~~ -> IbcMailServer
-

## Using the Justfile

This repository comes with a [`justfile`](https://github.com/casey/just), which is a handy task runner that helps with building, testing, and publishing your Abstract app module.
Expand All @@ -43,8 +59,8 @@ Here are some of the tasks available in the `justfile`:
- `publish {{chain-id}}`: Publish the App to a network.
- `wasm`: Optimize the contract.
- `schema`: Generate the json schemas for the contract
<!-- - `ts-codegen`: Generate the typescript client code for the contract -->
<!-- - `ts-publish`: Publish the typescript client code to npm -->
<!-- - `ts-codegen`: Generate the typescript app code for the contract -->
<!-- - `ts-publish`: Publish the typescript app code to npm -->
- `publish-schemas`: Publish the schemas by creating a PR on the Abstract [schemas](https://github.com/AbstractSDK/schemas) repository.

You can see the full list of tasks available by running `just --list`.
Expand Down
55 changes: 55 additions & 0 deletions contracts/adapter/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
[package]
name = "my-adapter"
version = "0.0.1"
authors = [
"CyberHoward <[email protected]>",
"Adair <[email protected]>",
"Abstract Money <[email protected]>",
]
edition = "2021"
homepage = ""
documentation = ""
repository = ""
license = "GPL-3.0-or-later"
keywords = ["cosmos", "cosmwasm", "abstractsdk"]
resolver = "2"

exclude = ["contract.wasm", "hash.txt"]

[lib]
crate-type = ["cdylib", "rlib"]

[[example]]
name = "schema"
required-features = ["schema"]

[features]
default = ["export"]
export = []
schema = ["abstract-adapter/schema"]

[dependencies]
cosmwasm-std = { workspace = true }
cosmwasm-schema = { workspace = true }
cw-controllers = { workspace = true }
cw-storage-plus = { workspace = true }
thiserror = { workspace = true }
schemars = { workspace = true }
cw-asset = { workspace = true }
abstract-adapter = { workspace = true }
const_format = { workspace = true }

# Dependencies for interface
cw-orch = { workspace = true }
abstract-interface = { workspace = true }

[dev-dependencies]
my-adapter = { workspace = true }
abstract-client = { workspace = true }
abstract-adapter = { workspace = true, features = ["test-utils"] }
speculoos = { workspace = true }
semver = { workspace = true }
dotenv = { workspace = true }
env_logger = { workspace = true }
cw-orch = { workspace = true, features = ["daemon"] }
clap = { workspace = true, features = ["derive"] }
91 changes: 91 additions & 0 deletions contracts/adapter/examples/local_daemon.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
//! Deploys Abstract and the Adapter module to a local Junod instance. See how to spin up a local chain here: https://docs.junonetwork.io/developer-guides/junod-local-dev-setup
//! You can also start a juno container by running `just juno-local`.
//!
//! Ensure the local juno is running before executing this script.
//! Also make sure port 9090 is exposed on the local juno container. This port is used to communicate with the chain.
//!
//! # Run
//!
//! `RUST_LOG=info cargo run --example local_daemon --package my-adapter`
use my_adapter::{contract::interface::MyAdapterInterface, MyAdapterExecuteMsg, MY_ADAPTER_ID};

use abstract_adapter::{objects::namespace::Namespace, std::adapter::AdapterRequestMsg};
use abstract_client::{AbstractClient, Publisher};
use cw_orch::{anyhow, prelude::*, tokio::runtime::Runtime};
use my_adapter::{msg::MyAdapterInstantiateMsg, ADAPTER_VERSION};
use semver::Version;

const LOCAL_MNEMONIC: &str = "clip hire initial neck maid actor venue client foam budget lock catalog sweet steak waste crater broccoli pipe steak sister coyote moment obvious choose";

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

let _version: Version = ADAPTER_VERSION.parse().unwrap();
let runtime = Runtime::new()?;

let daemon = Daemon::builder()
.chain(networks::LOCAL_JUNO)
.mnemonic(LOCAL_MNEMONIC)
.handle(runtime.handle())
.build()
.unwrap();

let adapter_namespace = Namespace::from_id(MY_ADAPTER_ID)?;

// Create an [`AbstractClient`]
// Note: AbstractClient Builder used because Abstract is not yet deployed on the chain
let abstract_client: AbstractClient<Daemon> =
AbstractClient::builder(daemon.clone()).build()?;

// Get the [`Publisher`] that owns the namespace.
// If there isn't one, it creates an Account and claims the namespace.
let publisher: Publisher<_> = abstract_client
.publisher_builder(adapter_namespace)
.build()?;

// Ensure the current sender owns the namespace
assert_eq!(
publisher.account().owner()?,
daemon.sender(),
"The current sender can not publish to this namespace. Please use the wallet that owns the Account that owns the Namespace."
);

// Publish the Adapter to the Abstract Platform
publisher.publish_adapter::<MyAdapterInstantiateMsg, MyAdapterInterface<Daemon>>(
MyAdapterInstantiateMsg {},
)?;

// Install the Adapter on a new account

let account = abstract_client.account_builder().build()?;
// Installs the adapter on the Account
let adapter = account.install_adapter::<MyAdapterInterface<_>>(&[])?;

// // Import adapter's endpoint function traits for easy interactions.
use my_adapter::msg::MyAdapterQueryMsgFns;
let status_response = adapter.status(adapter.account().id()?)?;
assert!(status_response.status.is_none());

// Execute the Adapter
adapter.execute(
&AdapterRequestMsg {
// Adapter need to know on which account action is performed
proxy_address: Some(adapter.account().proxy()?.to_string()),
request: MyAdapterExecuteMsg::SetStatus {
status: "new_status".to_owned(),
},
}
.into(),
None,
)?;

// Query the Adapter again
let status_response = adapter.status(adapter.account().id()?)?;
assert_eq!(status_response.status, Some("new_status".to_owned()));

// Note: the Adapter is installed on a sub-account of the main account!
assert_ne!(account.id()?, adapter.account().id()?);

Ok(())
}
69 changes: 69 additions & 0 deletions contracts/adapter/examples/publish.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//! Publishes the module to the Abstract platform by uploading it and registering it on the app store.
//!
//! Info: The mnemonic used to register the module must be the same as the owner of the account that claimed the namespace.
//!
//! ## Example
//!
//! ```bash
//! $ just publish uni-6 osmo-test-5
//! ```
use my_adapter::{
contract::interface::MyAdapterInterface, msg::MyAdapterInstantiateMsg, MY_ADAPTER_ID,
};

use abstract_adapter::objects::namespace::Namespace;
use abstract_client::{AbstractClient, Publisher};
use clap::Parser;
use cw_orch::{anyhow, daemon::networks::parse_network, prelude::*, tokio::runtime::Runtime};

fn publish(networks: Vec<ChainInfo>) -> anyhow::Result<()> {
// run for each requested network
for network in networks {
// Setup
let rt = Runtime::new()?;
let chain = DaemonBuilder::default()
.handle(rt.handle())
.chain(network)
.build()?;

let adapter_namespace = Namespace::from_id(MY_ADAPTER_ID)?;

// Create an [`AbstractClient`]
let abstract_client: AbstractClient<Daemon> = AbstractClient::new(chain.clone())?;

// Get the [`Publisher`] that owns the namespace, otherwise create a new one and claim the namespace
let publisher: Publisher<_> = abstract_client
.publisher_builder(adapter_namespace)
.build()?;

if publisher.account().owner()? != chain.sender() {
panic!("The current sender can not publish to this namespace. Please use the wallet that owns the Account that owns the Namespace.")
}

// Publish the Adapter to the Abstract Platform
publisher.publish_adapter::<MyAdapterInstantiateMsg, MyAdapterInterface<Daemon>>(
MyAdapterInstantiateMsg {},
)?;
}
Ok(())
}

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

fn main() {
dotenv::dotenv().ok();
env_logger::init();
let args = Arguments::parse();
let networks = args
.network_ids
.iter()
.map(|n| parse_network(n).unwrap())
.collect();
publish(networks).unwrap();
}
14 changes: 14 additions & 0 deletions contracts/adapter/examples/schema.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use cosmwasm_schema::remove_schemas;
use my_adapter::contract::MyAdapter;
use std::env::current_dir;
use std::fs::create_dir_all;

fn main() {
let mut out_dir = current_dir().unwrap();
out_dir.push("schema");
create_dir_all(&out_dir).unwrap();
remove_schemas(&out_dir).unwrap();

#[cfg(feature = "schema")]
MyAdapter::export_schema(&out_dir);
}
Loading

0 comments on commit 9f8edf7

Please sign in to comment.