diff --git a/Cargo.toml b/Cargo.toml index 4884070..fb24c7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,6 @@ cw-asset = { version = "4.0.0" } abstract-app = { version = "0.24.1" } abstract-adapter = { version = "0.24.1" } abstract-standalone = { version = "0.24.1" } -abstract-interface = { version = "0.24.1" } abstract-client = { version = "0.24.1" } cw-orch = { version = "0.27.0" } {% if include_ibc_app %} diff --git a/contracts/{{adapter_name}}/Cargo.toml b/contracts/{{adapter_name}}/Cargo.toml index cc336f2..6379def 100644 --- a/contracts/{{adapter_name}}/Cargo.toml +++ b/contracts/{{adapter_name}}/Cargo.toml @@ -48,7 +48,6 @@ const_format = { workspace = true } # Dependencies for interface cw-orch = { workspace = true } -abstract-interface = { workspace = true } # Dependencies for bins clap = { workspace = true, optional = true, features = ["derive"] } diff --git a/contracts/{{app_name}}/Cargo.toml b/contracts/{{app_name}}/Cargo.toml index bc3c774..106f4c4 100644 --- a/contracts/{{app_name}}/Cargo.toml +++ b/contracts/{{app_name}}/Cargo.toml @@ -48,7 +48,6 @@ const_format = { workspace = true } # Dependencies for interface cw-orch = { workspace = true } -abstract-interface = { workspace = true } # Dependencies for bins clap = { workspace = true, optional = true, features = ["derive"] } diff --git a/contracts/{{app_name}}/src/contract.rs b/contracts/{{app_name}}/src/contract.rs index 89cc677..88c825f 100644 --- a/contracts/{{app_name}}/src/contract.rs +++ b/contracts/{{app_name}}/src/contract.rs @@ -35,7 +35,7 @@ abstract_app::cw_orch_interface!(APP, {{app_name | upper_camel_case}}, {{app_nam // TODO: add to docmuentation // https://linear.app/abstract-sdk/issue/ABS-414/add-documentation-on-dependencycreation-trait #[cfg(not(target_arch = "wasm32"))] -impl abstract_interface::DependencyCreation +impl abstract_app::abstract_interface::DependencyCreation for crate::{{app_name | upper_camel_case}}Interface { type DependenciesConfig = cosmwasm_std::Empty; diff --git a/contracts/{{ibc_app_name}}/Cargo.toml b/contracts/{{ibc_app_name}}/Cargo.toml new file mode 100644 index 0000000..182be37 --- /dev/null +++ b/contracts/{{ibc_app_name}}/Cargo.toml @@ -0,0 +1,50 @@ +[package] +name = "ping-pong" +version = "0.1.0" +edition = "2021" + +[features] +default = ["export"] +export = [] +daemon-bin = [ + "cw-orch/daemon", + "dep:clap", + "dep:abstract-client", + "dep:dotenv", + "dep:env_logger", +] +schema = ["abstract-app/schema"] + +[lib] +crate-type = ["cdylib", "rlib"] +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[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-app = { workspace = true } +abstract-ibc-client = { version = "0.24.1", default-features = false } +const_format = { workspace = true } + +# Dependencies for interface +cw-orch = { workspace = true } +abstract-interface = { workspace = true } + +# Dependencies for bins +clap = { workspace = true, optional = true, features = ["derive"] } +abstract-client = { workspace = true, features = [ + "interchain", +], optional = true } +dotenv = { workspace = true, optional = true } +env_logger = { workspace = true, optional = true } + +[dev-dependencies] +cw-orch-interchain = { workspace = true, features = ["daemon"] } +cw-orch = { workspace = true, features = ["daemon"] } +abstract-client = { workspace = true, features = ["interchain"] } +env_logger = "0.11" diff --git a/contracts/{{ibc_app_name}}/metadata.json b/contracts/{{ibc_app_name}}/metadata.json new file mode 100644 index 0000000..544344a --- /dev/null +++ b/contracts/{{ibc_app_name}}/metadata.json @@ -0,0 +1,9 @@ +{ + "name": "MyIbcApp", + "description": "IBC app description.", + "website": "", + "docs": "", + "type": "app", + "icon": "GiTrade", + "enabled": true +} diff --git a/contracts/{{ibc_app_name}}/src/bin/publish.rs b/contracts/{{ibc_app_name}}/src/bin/publish.rs new file mode 100644 index 0000000..b67ff50 --- /dev/null +++ b/contracts/{{ibc_app_name}}/src/bin/publish.rs @@ -0,0 +1,65 @@ +//! 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 test-app uni-6 osmo-test-5 +//! ``` +use ping_pong::PING_PONG_ID; + +use abstract_app::objects::namespace::Namespace; +use abstract_client::{AbstractClient, Publisher}; +use clap::Parser; +use cw_orch::{anyhow, daemon::networks::parse_network, prelude::*, tokio::runtime::Runtime}; +use ping_pong::PingPongInterface; + +fn publish(networks: Vec) -> anyhow::Result<()> { + // run for each requested network + for network in networks { + // Setup + let rt = Runtime::new()?; + let chain = DaemonBuilder::new(network).handle(rt.handle()).build()?; + + let app_namespace = Namespace::from_id(PING_PONG_ID)?; + + // Create an [`AbstractClient`] + let abstract_client: AbstractClient = 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 + .account_builder() + .namespace(app_namespace) + .build()? + .publisher()?; + + if publisher.account().owner()? != chain.sender_addr() { + panic!("The current sender can not publish to this namespace. Please use the wallet that owns the Account that owns the Namespace.") + } + + // Publish the App to the Abstract Platform + publisher.publish_app::>()?; + } + 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, +} + +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(); +} diff --git a/contracts/{{ibc_app_name}}/src/bin/schema.rs b/contracts/{{ibc_app_name}}/src/bin/schema.rs new file mode 100644 index 0000000..3ab47ac --- /dev/null +++ b/contracts/{{ibc_app_name}}/src/bin/schema.rs @@ -0,0 +1,13 @@ +use cosmwasm_schema::remove_schemas; +use ping_pong::contract::PingPong; +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(); + + PingPong::export_schema(&out_dir); +} diff --git a/contracts/{{ibc_app_name}}/src/contract.rs b/contracts/{{ibc_app_name}}/src/contract.rs new file mode 100644 index 0000000..89baed0 --- /dev/null +++ b/contracts/{{ibc_app_name}}/src/contract.rs @@ -0,0 +1,60 @@ +use abstract_app::{objects::dependency::StaticDependency, std::IBC_CLIENT, AppContract}; +use cosmwasm_std::Response; + +use crate::{ + error::PingPongError, + handlers, ibc, + msg::{AppMigrateMsg, PingPongExecuteMsg, PingPongInstantiateMsg, PingPongQueryMsg}, + APP_VERSION, PING_PONG_ID, +}; + +/// The type of the result returned by your app's entry points. +pub type PingPongResult = Result; + +/// The type of the app that is used to build your app and access the Abstract SDK features. +pub type PingPong = AppContract< + PingPongError, + PingPongInstantiateMsg, + PingPongExecuteMsg, + PingPongQueryMsg, + AppMigrateMsg, +>; + +const APP: PingPong = PingPong::new(PING_PONG_ID, APP_VERSION, None) + .with_instantiate(handlers::instantiate_handler) + .with_execute(handlers::execute_handler) + .with_query(handlers::query_handler) + .with_dependencies(&[StaticDependency::new( + IBC_CLIENT, + &[abstract_ibc_client::contract::CONTRACT_VERSION], + )]) + .with_module_ibc(ibc::receive_module_ibc) + .with_ibc_callback(ibc::ibc_callback); + +// Export handlers +#[cfg(feature = "export")] +abstract_app::export_endpoints!(APP, PingPong); + +abstract_app::cw_orch_interface!(APP, PingPong, PingPongInterface); + +#[cfg(not(target_arch = "wasm32"))] +use abstract_app::std::account::ModuleInstallConfig; +#[cfg(not(target_arch = "wasm32"))] +impl abstract_app::abstract_interface::DependencyCreation + for crate::PingPongInterface +{ + type DependenciesConfig = cosmwasm_std::Empty; + + fn dependency_install_configs( + _configuration: Self::DependenciesConfig, + ) -> Result, abstract_app::abstract_interface::AbstractInterfaceError> + { + Ok(vec![ModuleInstallConfig::new( + abstract_app::objects::module::ModuleInfo::from_id( + IBC_CLIENT, + abstract_ibc_client::contract::CONTRACT_VERSION.into(), + )?, + None, + )]) + } +} diff --git a/contracts/{{ibc_app_name}}/src/error.rs b/contracts/{{ibc_app_name}}/src/error.rs new file mode 100644 index 0000000..e5b069c --- /dev/null +++ b/contracts/{{ibc_app_name}}/src/error.rs @@ -0,0 +1,32 @@ +use abstract_app::{ + objects::module::ModuleInfo, sdk::AbstractSdkError, std::AbstractError, + AppError as AbstractAppError, +}; +use cosmwasm_std::StdError; +use cw_asset::AssetError; +use cw_controllers::AdminError; +use thiserror::Error; + +#[derive(Error, Debug, PartialEq)] +pub enum PingPongError { + #[error(transparent)] + Std(#[from] StdError), + + #[error(transparent)] + Abstract(#[from] AbstractError), + + #[error(transparent)] + AbstractSdk(#[from] AbstractSdkError), + + #[error(transparent)] + Asset(#[from] AssetError), + + #[error(transparent)] + Admin(#[from] AdminError), + + #[error(transparent)] + DappError(#[from] AbstractAppError), + + #[error("Caller module is not a PingPong: {source_module}")] + UnauthorizedIbc { source_module: ModuleInfo }, +} diff --git a/contracts/{{ibc_app_name}}/src/handlers/execute.rs b/contracts/{{ibc_app_name}}/src/handlers/execute.rs new file mode 100644 index 0000000..e84309d --- /dev/null +++ b/contracts/{{ibc_app_name}}/src/handlers/execute.rs @@ -0,0 +1,49 @@ +use abstract_app::{ + objects::TruncatedChainId, + sdk::{IbcClient, IbcInterface}, + std::ibc::Callback, + traits::AbstractResponse, +}; +use cosmwasm_std::{CosmosMsg, DepsMut, Env, MessageInfo}; + +use crate::{ + contract::{PingPong, PingPongResult}, + msg::{PingPongCallbackMsg, PingPongExecuteMsg, PingPongIbcMsg}, +}; + +pub fn execute_handler( + deps: DepsMut, + env: Env, + _info: MessageInfo, + module: PingPong, + msg: PingPongExecuteMsg, +) -> PingPongResult { + match msg { + PingPongExecuteMsg::Ping { opponent_chain } => { + ping_pong(deps, &env, opponent_chain, module) + } + } +} + +pub(crate) fn ping_pong( + deps: DepsMut, + env: &Env, + opponent_chain: TruncatedChainId, + module: PingPong, +) -> PingPongResult { + // # ANCHOR: ibc_client + let self_module_info = module.module_info()?; + let ibc_client: IbcClient<_> = module.ibc_client(deps.as_ref(), env); + let ibc_action: CosmosMsg = ibc_client.module_ibc_action( + opponent_chain.clone(), + self_module_info, + // Start by playing a Ping + &PingPongIbcMsg::Ping {}, + Some(Callback::new(&PingPongCallbackMsg::Pinged { + opponent_chain, + })?), + )?; + // # ANCHOR_END: ibc_client + + Ok(module.response("ping").add_message(ibc_action)) +} diff --git a/contracts/{{ibc_app_name}}/src/handlers/instantiate.rs b/contracts/{{ibc_app_name}}/src/handlers/instantiate.rs new file mode 100644 index 0000000..9bad31e --- /dev/null +++ b/contracts/{{ibc_app_name}}/src/handlers/instantiate.rs @@ -0,0 +1,16 @@ +use cosmwasm_std::{DepsMut, Env, MessageInfo, Response}; + +use crate::{ + contract::{PingPong, PingPongResult}, + msg::PingPongInstantiateMsg, +}; + +pub fn instantiate_handler( + _deps: DepsMut, + _env: Env, + _info: MessageInfo, + _module: PingPong, + _msg: PingPongInstantiateMsg, +) -> PingPongResult { + Ok(Response::new()) +} diff --git a/contracts/{{ibc_app_name}}/src/handlers/mod.rs b/contracts/{{ibc_app_name}}/src/handlers/mod.rs new file mode 100644 index 0000000..d27bf0a --- /dev/null +++ b/contracts/{{ibc_app_name}}/src/handlers/mod.rs @@ -0,0 +1,7 @@ +pub mod execute; +pub mod instantiate; +pub mod query; + +pub use crate::handlers::{ + execute::execute_handler, instantiate::instantiate_handler, query::query_handler, +}; diff --git a/contracts/{{ibc_app_name}}/src/handlers/query.rs b/contracts/{{ibc_app_name}}/src/handlers/query.rs new file mode 100644 index 0000000..f7e8dbf --- /dev/null +++ b/contracts/{{ibc_app_name}}/src/handlers/query.rs @@ -0,0 +1,34 @@ +use cosmwasm_std::{to_json_binary, Binary, Deps, Env, StdResult}; + +use crate::{ + contract::{PingPong, PingPongResult}, + msg::{PingPongQueryMsg, StatusResponse}, + state::{PINGS, PONGS}, +}; + +pub fn query_handler( + deps: Deps, + _env: Env, + _module: &PingPong, + msg: PingPongQueryMsg, +) -> PingPongResult { + match msg { + PingPongQueryMsg::Status {} => to_json_binary(&query_status(deps)?), + } + .map_err(Into::into) +} + +fn query_status(deps: Deps) -> StdResult { + // Sum pings + let pings = PINGS + .range(deps.storage, None, None, cosmwasm_std::Order::Ascending) + .map(|result| result.map(|(_k, v)| v).unwrap_or_default()) + .sum(); + // Sum pongs + let pongs = PONGS + .range(deps.storage, None, None, cosmwasm_std::Order::Ascending) + .map(|result| result.map(|(_k, v)| v).unwrap_or_default()) + .sum(); + + Ok(StatusResponse { pings, pongs }) +} diff --git a/contracts/{{ibc_app_name}}/src/ibc/callback.rs b/contracts/{{ibc_app_name}}/src/ibc/callback.rs new file mode 100644 index 0000000..aa4703b --- /dev/null +++ b/contracts/{{ibc_app_name}}/src/ibc/callback.rs @@ -0,0 +1,41 @@ +use abstract_app::{ + sdk::AbstractResponse, + std::{ + ibc::{Callback, IbcResult}, + ABSTRACT_EVENT_TYPE, + }, +}; +use cosmwasm_std::{from_json, DepsMut, Env}; + +use crate::{ + contract::{PingPong, PingPongResult}, + msg::PingPongCallbackMsg, + state::PINGS, +}; + +pub fn ibc_callback( + deps: DepsMut, + _env: Env, + module: PingPong, + callback: Callback, + result: IbcResult, +) -> PingPongResult { + match from_json(callback.msg)? { + PingPongCallbackMsg::Pinged { opponent_chain } => { + let exec_events = result.get_execute_events()?; + + let pong = exec_events.into_iter().any(|e| { + e.ty == ABSTRACT_EVENT_TYPE + && e.attributes + .iter() + .any(|a| a.key == "action" && a.value == "pong") + }); + if pong { + PINGS.update(deps.storage, &opponent_chain, |l| { + PingPongResult::Ok(l.unwrap_or_default() + 1) + })?; + } + Ok(module.response("pong")) + } + } +} diff --git a/contracts/{{ibc_app_name}}/src/ibc/mod.rs b/contracts/{{ibc_app_name}}/src/ibc/mod.rs new file mode 100644 index 0000000..879cb2d --- /dev/null +++ b/contracts/{{ibc_app_name}}/src/ibc/mod.rs @@ -0,0 +1,5 @@ +mod callback; +mod module; + +pub use callback::ibc_callback; +pub use module::receive_module_ibc; diff --git a/contracts/{{ibc_app_name}}/src/ibc/module.rs b/contracts/{{ibc_app_name}}/src/ibc/module.rs new file mode 100644 index 0000000..6c74358 --- /dev/null +++ b/contracts/{{ibc_app_name}}/src/ibc/module.rs @@ -0,0 +1,34 @@ +use abstract_app::{sdk::AbstractResponse, std::ibc::ModuleIbcInfo}; +use cosmwasm_std::{ensure_eq, from_json, Binary, DepsMut, Env, Response}; + +use crate::{ + contract::{PingPong, PingPongResult}, + error::PingPongError, + msg::PingPongIbcMsg, + state::PONGS, +}; + +pub fn receive_module_ibc( + deps: DepsMut, + _env: Env, + module: PingPong, + source_module: ModuleIbcInfo, + msg: Binary, +) -> PingPongResult { + let this_module_info = module.module_info()?; + ensure_eq!( + source_module.module, + this_module_info, + PingPongError::UnauthorizedIbc { + source_module: source_module.module.clone() + } + ); + let ibc_msg: PingPongIbcMsg = from_json(msg)?; + match ibc_msg { + PingPongIbcMsg::Ping {} => PONGS.update(deps.storage, &source_module.chain, |pongs| { + PingPongResult::Ok(pongs.unwrap_or_default() + 1) + })?, + }; + + Ok(module.response("pong")) +} diff --git a/contracts/{{ibc_app_name}}/src/lib.rs b/contracts/{{ibc_app_name}}/src/lib.rs new file mode 100644 index 0000000..633b89a --- /dev/null +++ b/contracts/{{ibc_app_name}}/src/lib.rs @@ -0,0 +1,17 @@ +pub mod contract; +pub mod error; +mod handlers; +mod ibc; +pub mod msg; +pub mod state; + +pub use contract::interface::PingPongInterface; +pub use msg::{PingPongExecuteMsgFns, PingPongQueryMsgFns}; + +/// The version of your app +pub const APP_VERSION: &str = env!("CARGO_PKG_VERSION"); + +pub const TESTGEN_LOCAL_NAMESPACE: &str = "testgen-local"; +pub const PING_PONG_NAME: &str = "ping-pong"; +pub const PING_PONG_ID: &str = + const_format::concatcp!(TESTGEN_LOCAL_NAMESPACE, ":", PING_PONG_NAME); diff --git a/contracts/{{ibc_app_name}}/src/msg.rs b/contracts/{{ibc_app_name}}/src/msg.rs new file mode 100644 index 0000000..be5b0a0 --- /dev/null +++ b/contracts/{{ibc_app_name}}/src/msg.rs @@ -0,0 +1,46 @@ +use abstract_app::objects::TruncatedChainId; +use cosmwasm_schema::QueryResponses; + +use crate::contract::PingPong; + +// This is used for type safety and re-exporting the contract endpoint structs. +abstract_app::app_msg_types!(PingPong, PingPongExecuteMsg, PingPongQueryMsg); + +/// App instantiate message +#[cosmwasm_schema::cw_serde] +pub struct PingPongInstantiateMsg {} + +/// App execute messages +#[cosmwasm_schema::cw_serde] +#[derive(cw_orch::ExecuteFns)] +pub enum PingPongExecuteMsg { + /// Increment ping in this module and pong on its counterpart on another chain. + Ping { opponent_chain: TruncatedChainId }, +} + +/// App query messages +#[cosmwasm_schema::cw_serde] +#[derive(QueryResponses, cw_orch::QueryFns)] +pub enum PingPongQueryMsg { + #[returns(StatusResponse)] + Status {}, +} + +#[cosmwasm_schema::cw_serde] +pub enum PingPongIbcMsg { + Ping {}, +} + +#[cosmwasm_schema::cw_serde] +pub enum PingPongCallbackMsg { + Pinged { opponent_chain: TruncatedChainId }, +} + +#[cosmwasm_schema::cw_serde] +pub struct AppMigrateMsg {} + +#[cosmwasm_schema::cw_serde] +pub struct StatusResponse { + pub pings: u32, + pub pongs: u32, +} diff --git a/contracts/{{ibc_app_name}}/src/state.rs b/contracts/{{ibc_app_name}}/src/state.rs new file mode 100644 index 0000000..722c9b1 --- /dev/null +++ b/contracts/{{ibc_app_name}}/src/state.rs @@ -0,0 +1,5 @@ +use abstract_app::objects::TruncatedChainId; +use cw_storage_plus::Map; + +pub const PINGS: Map<&TruncatedChainId, u32> = Map::new("pings"); +pub const PONGS: Map<&TruncatedChainId, u32> = Map::new("pongs"); diff --git a/contracts/{{ibc_app_name}}/tests/ping_pong_tests.rs b/contracts/{{ibc_app_name}}/tests/ping_pong_tests.rs new file mode 100644 index 0000000..c34aad6 --- /dev/null +++ b/contracts/{{ibc_app_name}}/tests/ping_pong_tests.rs @@ -0,0 +1,165 @@ +use abstract_app::objects::namespace::Namespace; + +use abstract_client::{AbstractClient, AbstractInterchainClient, Application, RemoteAccount}; + +use abstract_app::std::objects::TruncatedChainId; + +use cw_orch::{anyhow, prelude::*}; +use cw_orch_interchain::prelude::*; + +use ping_pong::msg::{PingPongInstantiateMsg, StatusResponse}; +use ping_pong::PING_PONG_ID; +use ping_pong::{PingPongExecuteMsgFns, PingPongInterface, PingPongQueryMsgFns}; + +const JUNO: &str = "juno-1"; +const STARGAZE: &str = "stargaze-1"; + +#[allow(unused)] +struct PingPong> { + abs_juno: AbstractClient, + abs_stargaze: AbstractClient, + ping_pong: Application>, + remote_account: RemoteAccount, + mock_interchain: IbcEnv, +} + +impl PingPong { + /// Set up the test environment with two Accounts that has the App installed + fn setup() -> anyhow::Result> { + // Logger + let _ = env_logger::builder().is_test(true).try_init(); + + // Create a sender and mock env + let mock_interchain = + MockBech32InterchainEnv::new(vec![(JUNO, "juno"), (STARGAZE, "stargaze")]); + + let interchain_abstract = AbstractInterchainClient::deploy_mock(&mock_interchain)?; + + let abs_juno = interchain_abstract.client(JUNO)?; + let abs_stargaze = interchain_abstract.client(STARGAZE)?; + + let namespace = Namespace::from_id(PING_PONG_ID)?; + // Publish and install on both chains + let publisher_juno = abs_juno + .account_builder() + .namespace(namespace.clone()) + .build()? + .publisher()?; + publisher_juno.publish_app::>()?; + let app = publisher_juno + .account() + .install_app_with_dependencies::>( + &PingPongInstantiateMsg {}, + Empty {}, + &[], + )?; + + let publisher_stargaze = abs_stargaze + .account_builder() + .namespace(namespace) + .build()? + .publisher()?; + publisher_stargaze.publish_app::>()?; + + let remote_account = app + .account() + .remote_account_builder(mock_interchain.clone(), &abs_stargaze) + .install_app_with_dependencies::>( + &PingPongInstantiateMsg {}, + Empty {}, + )? + .build()?; + Ok(PingPong { + abs_juno, + abs_stargaze, + ping_pong: app, + remote_account, + mock_interchain, + }) + } +} + +#[test] +fn successful_install() -> anyhow::Result<()> { + let env = PingPong::setup()?; + let app1 = env.ping_pong; + + let status = app1.status()?; + assert_eq!(status, StatusResponse { pings: 0, pongs: 0 }); + + let app2 = env.remote_account.application::>()?; + + let status: StatusResponse = app2.status()?; + + assert_eq!(status, StatusResponse { pings: 0, pongs: 0 }); + Ok(()) +} + +#[test] +fn successful_ping() -> anyhow::Result<()> { + // Create a sender and mock env + let env = PingPong::setup()?; + let app = env.ping_pong; + let remote_app = env.remote_account.application::>()?; + + let status = app.status()?; + assert_eq!(status, StatusResponse { pings: 0, pongs: 0 }); + let status = remote_app.status()?; + assert_eq!(status, StatusResponse { pings: 0, pongs: 0 }); + + // juno pings stargaze + let pp = app.ping(TruncatedChainId::from_chain_id(STARGAZE))?; + env.mock_interchain.await_and_check_packets(JUNO, pp)?; + + // juno pinged, stargaze ponged. + let status = app.status()?; + assert_eq!(status, StatusResponse { pings: 1, pongs: 0 }); + let status = remote_app.status()?; + assert_eq!(status, StatusResponse { pings: 0, pongs: 1 }); + + // repeat + let pp = app.ping(TruncatedChainId::from_chain_id(STARGAZE))?; + env.mock_interchain.await_and_check_packets(JUNO, pp)?; + + let status = app.status()?; + assert_eq!(status, StatusResponse { pings: 2, pongs: 0 }); + let status = remote_app.status()?; + assert_eq!(status, StatusResponse { pings: 0, pongs: 2 }); + + Ok(()) +} + +#[test] +fn successful_ping_to_home_chain() -> anyhow::Result<()> { + // Create a sender and mock env + let env = PingPong::setup()?; + let app = env.ping_pong; + let remote_app = env.remote_account.application::>()?; + + // stargaze pings juno + // Note that `RemoteApplication` takes care of waiting for ibc + remote_app.execute( + &ping_pong::msg::PingPongExecuteMsg::Ping { + opponent_chain: TruncatedChainId::from_chain_id(JUNO), + } + .into(), + vec![], + )?; + + // stargaze pinged, juno ponged + let status = app.status()?; + assert_eq!(status, StatusResponse { pings: 0, pongs: 1 }); + let status = remote_app.status()?; + assert_eq!(status, StatusResponse { pings: 1, pongs: 0 }); + + // juno ping, stargaze pong + let pp = app.ping(TruncatedChainId::from_chain_id(STARGAZE))?; + env.mock_interchain.await_and_check_packets(JUNO, pp)?; + + let status = app.status()?; + assert_eq!(status, StatusResponse { pings: 1, pongs: 1 }); + let status = remote_app.status()?; + assert_eq!(status, StatusResponse { pings: 1, pongs: 1 }); + + Ok(()) +} diff --git a/contracts/{{standalone_name}}/Cargo.toml b/contracts/{{standalone_name}}/Cargo.toml index 7bcab67..1964af7 100644 --- a/contracts/{{standalone_name}}/Cargo.toml +++ b/contracts/{{standalone_name}}/Cargo.toml @@ -48,7 +48,6 @@ const_format = { version = "0.2.32" } # Dependencies for interface cw-orch = { workspace = true } -abstract-interface = { workspace = true } # Dependencies for bins clap = { workspace = true, optional = true, features = ["derive"] } diff --git a/contracts/{{standalone_name}}/src/interface.rs b/contracts/{{standalone_name}}/src/interface.rs index 0f5f8bc..f7b9e10 100644 --- a/contracts/{{standalone_name}}/src/interface.rs +++ b/contracts/{{standalone_name}}/src/interface.rs @@ -15,13 +15,13 @@ use crate::{ )] pub struct {{standalone_name | upper_camel_case}}Interface; -impl abstract_interface::DependencyCreation +impl abstract_standalone::abstract_interface::DependencyCreation for {{standalone_name | upper_camel_case}}Interface { type DependenciesConfig = cosmwasm_std::Empty; } -impl abstract_interface::RegisteredModule +impl abstract_standalone::abstract_interface::RegisteredModule for {{standalone_name | upper_camel_case}}Interface { type InitMsg = <{{standalone_name | upper_camel_case}}Interface as InstantiableContract>::InstantiateMsg;