use cosmwasm_schema::QueryResponses;
use cosmwasm_std::{Addr, Binary, CosmosMsg, Deps, Empty, QueryRequest, StdError, Uint64};
use self::state::IbcInfrastructure;
use crate::{
account::{self, ModuleInstallConfig},
ibc::{Callback, ModuleQuery},
ibc_host::HostAction,
objects::{
account::AccountId, module::ModuleInfo, module_reference::ModuleReference,
registry::RegistryContract, TruncatedChainId,
},
AbstractError,
};
use super::{polytone_callbacks, IBCLifecycleComplete};
pub mod state {
use cosmwasm_std::{Addr, Binary, Coin};
use cw_storage_plus::{Item, Map};
use crate::{
ibc::ICS20PacketIdentifier,
objects::{
account::{AccountSequence, AccountTrace},
storage_namespaces, TruncatedChainId,
},
};
#[cosmwasm_schema::cw_serde]
pub struct IbcInfrastructure {
pub polytone_note: Addr,
pub remote_abstract_host: String,
pub remote_proxy: Option<String>,
}
#[cosmwasm_schema::cw_serde]
pub struct AccountCallbackPayload {
pub channel_id: String,
pub account_address: Addr,
pub funds: Coin,
pub msgs: Vec<Binary>,
}
pub const IBC_INFRA: Map<&TruncatedChainId, IbcInfrastructure> =
Map::new(storage_namespaces::ibc_client::IBC_INFRA);
pub const REVERSE_POLYTONE_NOTE: Map<&Addr, TruncatedChainId> =
Map::new(storage_namespaces::ibc_client::REVERSE_POLYTONE_NOTE);
pub const ACCOUNTS: Map<(&AccountTrace, AccountSequence, &TruncatedChainId), String> =
Map::new(storage_namespaces::ibc_client::ACCOUNTS);
pub const ACKS: Item<Vec<String>> = Item::new(storage_namespaces::ibc_client::ACKS);
pub const ICS20_ACCOUNT_CALLBACKS: Map<ICS20PacketIdentifier, (Addr, Coin, Vec<Binary>)> =
Map::new(storage_namespaces::ibc_client::ICS20_ACCOUNT_CALLBACKS);
pub const ICS20_ACCOUNT_CALLBACK_PAYLOAD: Item<AccountCallbackPayload> =
Item::new(storage_namespaces::ibc_client::ICS20_ACCOUNT_CALLBACK_PAYLOAD);
}
#[cosmwasm_schema::cw_serde]
pub struct InstantiateMsg {}
#[cosmwasm_schema::cw_serde]
pub struct MigrateMsg {}
#[cosmwasm_schema::cw_serde]
#[derive(cw_orch::ExecuteFns)]
pub enum ExecuteMsg {
UpdateOwnership(cw_ownable::Action),
RegisterInfrastructure {
chain: TruncatedChainId,
note: String,
host: String,
},
SendFunds {
host_chain: TruncatedChainId,
receiver: Option<String>,
memo: Option<String>,
},
SendFundsWithActions {
host_chain: TruncatedChainId,
actions: Vec<Binary>,
},
Register {
host_chain: TruncatedChainId,
namespace: Option<String>,
install_modules: Vec<ModuleInstallConfig>,
},
ModuleIbcAction {
host_chain: TruncatedChainId,
target_module: ModuleInfo,
msg: Binary,
callback: Option<Callback>,
},
IbcQuery {
host_chain: TruncatedChainId,
queries: Vec<QueryRequest<ModuleQuery>>,
callback: Callback,
},
RemoteAction {
host_chain: TruncatedChainId,
action: HostAction,
},
RemoveHost { host_chain: TruncatedChainId },
Callback(polytone_callbacks::CallbackMessage),
}
#[cosmwasm_schema::cw_serde]
pub enum PolytoneNoteExecuteMsg {
Query {
msgs: Vec<QueryRequest<Empty>>,
callback: polytone_callbacks::CallbackRequest,
timeout_seconds: Uint64,
},
Execute {
msgs: Vec<CosmosMsg<Empty>>,
callback: Option<polytone_callbacks::CallbackRequest>,
timeout_seconds: Uint64,
},
}
#[cosmwasm_schema::cw_serde]
pub enum IbcClientCallback {
ModuleRemoteAction {
sender_address: String,
callback: Callback,
initiator_msg: Binary,
},
ModuleRemoteQuery {
sender_address: String,
callback: Callback,
queries: Vec<QueryRequest<ModuleQuery>>,
},
CreateAccount {
account_id: AccountId,
},
WhoAmI {},
}
#[cosmwasm_schema::cw_serde]
pub struct InstalledModuleIdentification {
pub module_info: ModuleInfo,
pub account_id: Option<AccountId>,
}
#[cosmwasm_schema::cw_serde]
pub struct ModuleAddr {
pub reference: ModuleReference,
pub address: Addr,
}
impl InstalledModuleIdentification {
pub fn addr(
&self,
deps: Deps,
registry: RegistryContract,
) -> Result<ModuleAddr, AbstractError> {
let target_module_resolved =
registry.query_module(self.module_info.clone(), &deps.querier)?;
let no_account_id_error =
StdError::generic_err("Account id not specified in installed module definition");
let target_addr = match &target_module_resolved.reference {
ModuleReference::Account(code_id) => {
let target_account_id = self.account_id.clone().ok_or(no_account_id_error)?;
let account = registry.account(&target_account_id, &deps.querier)?;
if deps
.querier
.query_wasm_contract_info(account.addr().as_str())?
.code_id
== *code_id
{
account.into_addr()
} else {
Err(StdError::generic_err(
"Account contract doesn't correspond to code id of the account",
))?
}
}
ModuleReference::Native(addr)
| ModuleReference::Adapter(addr)
| ModuleReference::Service(addr) => addr.clone(),
ModuleReference::App(_) | ModuleReference::Standalone(_) => {
let target_account_id = self.account_id.clone().ok_or(no_account_id_error)?;
let account = registry.account(&target_account_id, &deps.querier)?;
let module_info: account::ModuleAddressesResponse = deps.querier.query_wasm_smart(
account.into_addr(),
&account::QueryMsg::ModuleAddresses {
ids: vec![self.module_info.id()],
},
)?;
module_info
.modules
.first()
.ok_or(AbstractError::AppNotInstalled(self.module_info.to_string()))?
.1
.clone()
}
};
Ok(ModuleAddr {
reference: target_module_resolved.reference,
address: target_addr,
})
}
}
#[cosmwasm_schema::cw_serde]
#[derive(QueryResponses, cw_orch::QueryFns)]
pub enum QueryMsg {
#[returns(cw_ownable::Ownership<Addr> )]
Ownership {},
#[returns(ConfigResponse)]
Config {},
#[returns(HostResponse)]
Host { chain_name: TruncatedChainId },
#[returns(ListAccountsResponse)]
ListAccounts {
start: Option<(AccountId, String)>,
limit: Option<u32>,
},
#[returns(AccountResponse)]
#[cw_orch(fn_name("remote_account"))]
Account {
chain_name: TruncatedChainId,
account_id: AccountId,
},
#[returns(ListRemoteHostsResponse)]
ListRemoteHosts {},
#[returns(ListRemoteProxiesResponse)]
ListRemoteProxies {},
#[returns(ListRemoteAccountsResponse)]
ListRemoteAccountsByAccountId { account_id: AccountId },
#[returns(ListIbcInfrastructureResponse)]
ListIbcInfrastructures {},
}
#[cosmwasm_schema::cw_serde]
pub enum SudoMsg {
#[serde(rename = "ibc_lifecycle_complete")]
IBCLifecycleComplete(IBCLifecycleComplete),
}
#[cosmwasm_schema::cw_serde]
pub struct ConfigResponse {
pub ans_host: Addr,
pub registry_address: Addr,
}
#[cosmwasm_schema::cw_serde]
pub struct ListAccountsResponse {
pub accounts: Vec<(AccountId, TruncatedChainId, String)>,
}
#[cosmwasm_schema::cw_serde]
pub struct ListRemoteHostsResponse {
pub hosts: Vec<(TruncatedChainId, String)>,
}
#[cosmwasm_schema::cw_serde]
pub struct ListRemoteAccountsResponse {
pub accounts: Vec<(TruncatedChainId, Option<String>)>,
}
pub type ListRemoteProxiesResponse = ListRemoteAccountsResponse;
#[cosmwasm_schema::cw_serde]
pub struct ListIbcInfrastructureResponse {
pub counterparts: Vec<(TruncatedChainId, IbcInfrastructure)>,
}
#[cosmwasm_schema::cw_serde]
pub struct HostResponse {
pub remote_host: String,
pub remote_polytone_proxy: Option<String>,
}
#[cosmwasm_schema::cw_serde]
pub struct AccountResponse {
pub remote_account_addr: Option<String>,
}
#[cfg(test)]
mod tests {
use cosmwasm_std::{to_json_binary, CosmosMsg, Empty};
use crate::app::ExecuteMsg;
use crate::ibc::{Callback, IbcResponseMsg, IbcResult};
#[coverage_helper::test]
fn test_response_msg_to_callback_msg() {
let receiver = "receiver".to_string();
let result = IbcResult::FatalError("ibc execution error".to_string());
let response_msg = IbcResponseMsg {
callback: Callback::new(&String::from("15")).unwrap(),
result,
};
let actual: CosmosMsg<Empty> = response_msg
.clone()
.into_cosmos_msg(receiver.clone())
.unwrap();
assert_eq!(
actual,
CosmosMsg::Wasm(cosmwasm_std::WasmMsg::Execute {
contract_addr: receiver,
msg: to_json_binary(&ExecuteMsg::<Empty>::IbcCallback(response_msg)).unwrap(),
funds: vec![],
})
)
}
}