Skip to content

Delivery Status Notifications (advanced IBC) #18

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
15 changes: 9 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ members = ["contracts/*", "packages/ibcmail", "tests"]
resolver = "2"

[workspace.package]
version = "0.2.0"
version = "0.3.0"

[workspace.dependencies]
cosmwasm-std = "1.5.3"
Expand All @@ -12,6 +12,7 @@ cw-controllers = "1.1.2"
cw-storage-plus = "1.2.0"
thiserror = "1.0.50"
cw-paginate = "0.2.1"
cw-item-set = "0.7.1"
schemars = "0.8"
cw-asset = "3.0.0"
cw-semver = { version = "1.0", features = ["serde"] }
Expand All @@ -20,14 +21,16 @@ cw-orch = "0.24.1"
ibcmail = { path = "packages/ibcmail", package = "ibcmail" }
client = { path = "contracts/client", package = "ibcmail-client" }
server = { path = "contracts/server", package = "ibcmail-server" }
abstract-client = "0.23.0"
abstract-app = "0.23.0"
abstract-adapter = "0.23.0"
abstract-interface = "0.23.0"

abstract-client = "=0.23.0"
abstract-app = "=0.23.0"
abstract-adapter = "=0.23.0"
abstract-interface = "0.23.1"

speculoos = "0.11.0"
semver = "1.0"
dotenv = "0.15.0"
env_logger = "0.10.0"
env_logger = "0.11.3"
clap = "4.3.7"
const_format = "0.2.32"

Expand Down
1 change: 0 additions & 1 deletion contracts/client/examples/publish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
//! $ just publish uni-6 osmo-test-5
//! ```

use abstract_app::objects::module::ModuleVersion;
use abstract_app::objects::namespace::Namespace;
use abstract_client::{AbstractClient, Publisher};
use clap::Parser;
Expand Down
1 change: 1 addition & 0 deletions contracts/client/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::{dependencies::MAIL_SERVER_DEP, error::ClientError, handlers, APP_VER
pub type ClientResult<T = Response> = Result<T, ClientError>;

const APP: App = App::new(IBCMAIL_CLIENT_ID, APP_VERSION, None)
.with_instantiate(handlers::instantiate_handler)
.with_execute(handlers::execute_handler)
.with_query(handlers::query_handler)
.with_migrate(handlers::migrate_handler)
Expand Down
8 changes: 6 additions & 2 deletions contracts/client/src/dependencies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use abstract_app::{objects::module::ModuleInfo, std::manager::ModuleInstallConfi
use ibcmail::IBCMAIL_SERVER_ID;

pub const MAIL_SERVER_DEP: StaticDependency =
StaticDependency::new(IBCMAIL_SERVER_ID, &[">=0.0.1"]);
StaticDependency::new(IBCMAIL_SERVER_ID, &[">=0.3.0"]);

#[cfg(feature = "interface")]
impl<Chain: cw_orch::environment::CwEnv> abstract_app::abstract_interface::DependencyCreation
Expand All @@ -21,6 +21,10 @@ impl<Chain: cw_orch::environment::CwEnv> abstract_app::abstract_interface::Depen
None,
);

Ok(vec![adapter_install_config])
// The IBC client is depended upon by the server
let ibc_client =
ModuleInstallConfig::new(ModuleInfo::from_id_latest("abstract:ibc-client")?, None);

Ok(vec![adapter_install_config, ibc_client])
}
}
121 changes: 82 additions & 39 deletions contracts/client/src/handlers/execute.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
use crate::{
contract::{App, ClientResult},
error::ClientError,
msg::ClientExecuteMsg,
};
use abstract_app::objects::TruncatedChainId;
use abstract_app::{
sdk::ModuleRegistryInterface,
traits::{AbstractResponse, AccountIdentification},
};
use base64::prelude::*;
use cosmwasm_std::{ensure_eq, CosmosMsg, Deps, DepsMut, Env, MessageInfo};
use cosmwasm_std::{ensure_eq, Addr, CosmosMsg, Deps, DepsMut, Env, MessageInfo};
use ibcmail::client::state::SENT_STATUS;
use ibcmail::{
client::{
state::{RECEIVED, SENT},
ClientApp,
},
server::api::{MailServer, ServerInterface},
IbcMailMessage, Message, Recipient, Route, Sender, IBCMAIL_SERVER_ID,
};

use crate::{
contract::{App, ClientResult},
error::ClientError,
msg::ClientExecuteMsg,
ClientMetadata, DeliveryStatus, Header, MailMessage, MessageHash, ReceivedMessage, Recipient,
Sender, IBCMAIL_SERVER_ID,
};

// # ANCHOR: execute_handler
Expand All @@ -29,10 +30,15 @@ pub fn execute_handler(
msg: ClientExecuteMsg,
) -> ClientResult {
match msg {
ClientExecuteMsg::SendMessage { message, route } => {
send_msg(deps, env, info, message, route, app)
ClientExecuteMsg::SendMessage {
message,
recipient,
metadata,
} => send_msg(deps, env, info, app, message, recipient, metadata),
ClientExecuteMsg::ReceiveMessage(message) => receive_msg(deps, info, app, message),
ClientExecuteMsg::UpdateDeliveryStatus { id, status } => {
update_delivery_status(deps, info, app, id, status)
}
ClientExecuteMsg::ReceiveMessage(message) => receive_msg(deps, info, message, app),
}
}
// # ANCHOR_END: execute_handler
Expand All @@ -42,60 +48,97 @@ fn send_msg(
deps: DepsMut,
env: Env,
_info: MessageInfo,
msg: Message,
route: Option<Route>,
app: ClientApp,
message: MailMessage,
recipient: Recipient,
metadata: Option<ClientMetadata>,
) -> ClientResult {
// validate basic fields of message, construct message to send to server
let to_hash = format!("{:?}{:?}{:?}", env.block.time, msg.subject, msg.recipient);
let to_hash = format!("{:?}{:?}{:?}", env.block.time, message.subject, recipient);
let hash = <sha2::Sha256 as sha2::Digest>::digest(to_hash);
let base_64_hash = BASE64_STANDARD.encode(hash);
let to_send = IbcMailMessage {

let sender = Sender::account(
app.account_id(deps.as_ref()).unwrap(),
Some(TruncatedChainId::new(&env)),
);
let version = app.version().to_string();

let client_header = Header {
sender,
recipient,
id: base_64_hash,
sender: Sender::account(
app.account_id(deps.as_ref()).unwrap(),
Some(TruncatedChainId::new(&env)),
),
message: Message {
recipient: msg.recipient,
subject: msg.subject,
body: msg.body,
},
version,
timestamp: env.block.time,
version: app.version().to_string(),
reply_to: None,
};

SENT.save(deps.storage, to_send.id.clone(), &to_send)?;
SENT.save(
deps.storage,
client_header.id.clone(),
&(message.clone(), client_header.clone()),
)?;

let server: MailServer<_> = app.mail_server(deps.as_ref());
let route_msg: CosmosMsg = server.process_msg(to_send, route)?;
let route_msg: CosmosMsg = server.process_msg(message, client_header, metadata)?;

Ok(app.response("send").add_message(route_msg))
}
// # ANCHOR_END: send_msg

/// Receive a message from the server
// # ANCHOR: receive_msg
fn receive_msg(deps: DepsMut, info: MessageInfo, msg: IbcMailMessage, app: App) -> ClientResult {
fn receive_msg(
deps: DepsMut,
info: MessageInfo,
app: App,
received: ReceivedMessage,
) -> ClientResult {
ensure_server_sender(deps.as_ref(), &app, info.sender)?;
ensure_correct_recipient(deps.as_ref(), &received.header.recipient, &app)?;

let msg_id = received.header.id.clone();
RECEIVED.save(deps.storage, msg_id.clone(), &received)?;

Ok(app
.response("received")
.add_attribute("message_id", &msg_id))
}
// # ANCHOR_END: receive_msg

fn update_delivery_status(
deps: DepsMut,
info: MessageInfo,
app: App,
id: MessageHash,
status: DeliveryStatus,
) -> ClientResult {
ensure_server_sender(deps.as_ref(), &app, info.sender)?;

// ensure that the message exists
SENT.load(deps.storage, id.clone())
.map_err(|_| ClientError::MessageNotFound(id.clone()))?;
SENT_STATUS.save(deps.storage, id.clone(), &status)?;

Ok(app
.response("update_msg_status")
.add_attribute("message_id", &id)
.add_attribute("status", status.to_string()))
}

fn ensure_server_sender(deps: Deps, app: &ClientApp, sender: Addr) -> Result<(), ClientError> {
let sender_module = app
.module_registry(deps.as_ref())?
.module_info(info.sender)
.module_registry(deps)?
.module_info(sender)
.map_err(|_| ClientError::NotMailServer {})?;

ensure_eq!(
sender_module.info.id(),
IBCMAIL_SERVER_ID,
ClientError::NotMailServer {}
);

ensure_correct_recipient(deps.as_ref(), &msg.message.recipient, &app)?;

RECEIVED.save(deps.storage, msg.id.clone(), &msg)?;

Ok(app
.response("received")
.add_attribute("message_id", &msg.id))
Ok(())
}
// # ANCHOR_END: receive_msg

fn ensure_correct_recipient(
deps: Deps,
Expand Down
24 changes: 24 additions & 0 deletions contracts/client/src/handlers/instantiate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use crate::{
contract::{App, ClientResult},
CLIENT_FEATURES,
};
use abstract_app::traits::AbstractResponse;
use cosmwasm_std::{DepsMut, Env, MessageInfo};
use ibcmail::client::msg::ClientInstantiateMsg;
use ibcmail::client::state::FEATURES;

pub fn instantiate_handler(
deps: DepsMut,
_env: Env,
info: MessageInfo,
app: App,
_msg: ClientInstantiateMsg,
) -> ClientResult {
for feature in CLIENT_FEATURES {
FEATURES.save(deps.storage, feature.to_string(), &true)?;
}

Ok(app
.response("instantiate")
.add_attribute("features", CLIENT_FEATURES.join(",")))
}
4 changes: 3 additions & 1 deletion contracts/client/src/handlers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
pub mod execute;
pub mod instantiate;
pub mod migrate;
pub mod query;

pub use crate::handlers::{
execute::execute_handler, migrate::migrate_handler, query::query_handler,
execute::execute_handler, instantiate::instantiate_handler, migrate::migrate_handler,
query::query_handler,
};
Loading
Loading