use cosmwasm_schema::QueryResponses;
use cosmwasm_std::{Addr, Binary, Coin, Deps, Empty, QueryRequest, StdError};
use polytone::callbacks::CallbackMessage;
use self::state::IbcInfrastructure;
use crate::{
ibc::CallbackInfo,
ibc_host::HostAction,
manager::{self, ModuleInstallConfig},
objects::{
account::AccountId, chain_name::ChainName, module::ModuleInfo,
module_reference::ModuleReference, version_control::VersionControlContract, AssetEntry,
},
AbstractError,
};
pub mod state {
use cosmwasm_std::Addr;
use cw_storage_plus::{Item, Map};
use crate::objects::{
account::{AccountSequence, AccountTrace},
ans_host::AnsHost,
chain_name::ChainName,
version_control::VersionControlContract,
};
#[cosmwasm_schema::cw_serde]
pub struct Config {
pub version_control: VersionControlContract,
pub ans_host: AnsHost,
}
#[cosmwasm_schema::cw_serde]
pub struct IbcInfrastructure {
pub polytone_note: Addr,
pub remote_abstract_host: String,
pub remote_proxy: Option<String>,
}
pub const IBC_INFRA: Map<&ChainName, IbcInfrastructure> = Map::new("ibci");
pub const REVERSE_POLYTONE_NOTE: Map<&Addr, ChainName> = Map::new("revpn");
pub const CONFIG: Item<Config> = Item::new("config");
pub const ACCOUNTS: Map<(&AccountTrace, AccountSequence, &ChainName), String> =
Map::new("accs");
pub const ACKS: Item<Vec<String>> = Item::new("tmpc");
}
#[cosmwasm_schema::cw_serde]
pub struct InstantiateMsg {
pub ans_host_address: String,
pub version_control_address: String,
}
#[cosmwasm_schema::cw_serde]
pub struct MigrateMsg {}
#[cosmwasm_schema::cw_serde]
#[derive(cw_orch::ExecuteFns)]
pub enum ExecuteMsg {
UpdateOwnership(cw_ownable::Action),
RegisterInfrastructure {
chain: String,
note: String,
host: String,
},
UpdateConfig {
ans_host: Option<String>,
version_control: Option<String>,
},
SendFunds {
host_chain: String,
funds: Vec<Coin>,
},
Register {
host_chain: String,
base_asset: Option<AssetEntry>,
namespace: Option<String>,
install_modules: Vec<ModuleInstallConfig>,
},
ModuleIbcAction {
host_chain: String,
target_module: ModuleInfo,
msg: Binary,
callback_info: Option<CallbackInfo>,
},
IbcQuery {
host_chain: String,
query: QueryRequest<Empty>,
callback_info: CallbackInfo,
},
RemoteAction {
host_chain: String,
action: HostAction,
},
RemoveHost {
host_chain: String,
},
Callback(CallbackMessage),
}
#[cosmwasm_schema::cw_serde]
pub enum IbcClientCallback {
ModuleRemoteAction {
sender_address: String,
callback_info: CallbackInfo,
initiator_msg: Binary,
},
ModuleRemoteQuery {
sender_address: String,
callback_info: CallbackInfo,
query: QueryRequest<Empty>,
},
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,
vc: VersionControlContract,
) -> Result<ModuleAddr, AbstractError> {
let target_module_resolved = vc.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::AccountBase(code_id) => {
let target_account_id = self.account_id.clone().ok_or(no_account_id_error)?;
let account_base = vc.account_base(&target_account_id, &deps.querier)?;
if deps
.querier
.query_wasm_contract_info(&account_base.proxy)?
.code_id
== *code_id
{
account_base.proxy
} else if deps
.querier
.query_wasm_contract_info(&account_base.manager)?
.code_id
== *code_id
{
account_base.manager
} else {
Err(StdError::generic_err(
"Account base contract doesn't correspond to any of the proxy or manager",
))?
}
}
ModuleReference::Native(addr) => addr.clone(),
ModuleReference::Adapter(addr) => addr.clone(),
ModuleReference::App(_) | ModuleReference::Standalone(_) => {
let target_account_id = self.account_id.clone().ok_or(no_account_id_error)?;
let account_base = vc.account_base(&target_account_id, &deps.querier)?;
let module_info: manager::ModuleAddressesResponse = deps.querier.query_wasm_smart(
account_base.manager,
&manager::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: String },
#[returns(ListAccountsResponse)]
ListAccounts {
start: Option<(AccountId, String)>,
limit: Option<u32>,
},
#[returns(AccountResponse)]
Account {
chain: String,
account_id: AccountId,
},
#[returns(ListRemoteHostsResponse)]
ListRemoteHosts {},
#[returns(ListRemoteProxiesResponse)]
ListRemoteProxies {},
#[returns(ListRemoteProxiesResponse)]
ListRemoteProxiesByAccountId { account_id: AccountId },
#[returns(ListIbcInfrastructureResponse)]
ListIbcInfrastructures {},
}
#[cosmwasm_schema::cw_serde]
pub struct ConfigResponse {
pub ans_host: String,
pub version_control_address: String,
}
#[cosmwasm_schema::cw_serde]
pub struct ListAccountsResponse {
pub accounts: Vec<(AccountId, ChainName, String)>,
}
#[cosmwasm_schema::cw_serde]
pub struct ListRemoteHostsResponse {
pub hosts: Vec<(ChainName, String)>,
}
#[cosmwasm_schema::cw_serde]
pub struct ListRemoteProxiesResponse {
pub proxies: Vec<(ChainName, Option<String>)>,
}
#[cosmwasm_schema::cw_serde]
pub struct ListIbcInfrastructureResponse {
pub counterparts: Vec<(ChainName, 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_proxy_addr: String,
}
#[cosmwasm_schema::cw_serde]
pub struct RemoteProxyResponse {
pub channel_id: String,
pub proxy_address: String,
}
#[cfg(test)]
mod tests {
use cosmwasm_std::{to_json_binary, CosmosMsg, Empty};
use speculoos::prelude::*;
use crate::app::ExecuteMsg;
use crate::ibc::{CallbackResult, IbcResponseMsg};
#[test]
fn test_response_msg_to_callback_msg() {
let receiver = "receiver".to_string();
let callback_id = "15".to_string();
let callback_msg = to_json_binary("15").unwrap();
let result = CallbackResult::FatalError("ibc execution error".to_string());
let response_msg = IbcResponseMsg {
id: callback_id,
msg: Some(callback_msg),
result,
};
let actual: CosmosMsg<Empty> = response_msg
.clone()
.into_cosmos_msg(receiver.clone())
.unwrap();
assert_that!(actual).is_equal_to(CosmosMsg::Wasm(cosmwasm_std::WasmMsg::Execute {
contract_addr: receiver,
msg: to_json_binary(&ExecuteMsg::<Empty, Empty>::IbcCallback(response_msg)).unwrap(),
funds: vec![],
}))
}
}