From dd24f21d44adf9a61202ccf5fd02b35643cf805e Mon Sep 17 00:00:00 2001 From: nhpd Date: Wed, 11 Oct 2023 17:35:30 +0400 Subject: [PATCH] draft: make contract work with errors, and independent ica creation --- src/contract.rs | 124 ++++++++++++++++++++++++++---------------------- src/helpers.rs | 1 - src/msg.rs | 8 ++-- src/state.rs | 4 +- 4 files changed, 73 insertions(+), 64 deletions(-) diff --git a/src/contract.rs b/src/contract.rs index 20ad221..96bdd2d 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -36,7 +36,6 @@ const NEUTRON_DENOM: &str = "untrn"; const TRANSFER_PORT: &str = "transfer"; const MSG_FUND_COMMUNITY_POOL: &str = "/cosmos.distribution.v1beta1.MsgFundCommunityPool"; -const MSG_IBC_TRANSFER: &str = "/ibc.applications.transfer.v2.FungibleTokenPacketData"; const SEND_TO_ICA_MEMO: &str = "transfer unclaimed airdrop to Cosmos Hub"; const FUND_COMMUNITY_POOL_MEMO: &str = "fund community pool from neutron unclaimed airdrop"; @@ -62,6 +61,7 @@ pub fn instantiate( ibc_neutron_denom: msg.ibc_neutron_denom, }, )?; + INTERCHAIN_ACCOUNT.save(deps.storage, &None)?; INTERCHAIN_TX_IN_PROGRESS.save(deps.storage, &false)?; Ok(Response::default()) @@ -83,24 +83,42 @@ pub fn execute( let current_stage = STAGE.load(deps.storage)?; match msg { - ExecuteMsg::ClaimUnclaimed {} => { - execute_claim_unclaimed(deps, env, current_stage) - } - ExecuteMsg::SendClaimedTokensToICA { } => { + ExecuteMsg::CreateHubICA {} => execute_create_hub_ica(deps, env, info), + ExecuteMsg::ClaimUnclaimed {} => execute_claim_unclaimed(deps, env, info, current_stage), + ExecuteMsg::SendClaimedTokensToICA {} => { execute_send_claimed_tokens_to_ica(deps, env, info, current_stage) } - ExecuteMsg::FundCommunityPool { } => { + ExecuteMsg::FundCommunityPool {} => { execute_fund_community_pool(deps, env, info, current_stage) } - ExecuteMsg::CreateHubICA { } => { - execute_create_hub_ica(deps, env) - } } } +fn execute_create_hub_ica( + deps: DepsMut, + _env: Env, + _info: MessageInfo, +) -> NeutronResult> { + // return if ICA channel exists and opened + if INTERCHAIN_ACCOUNT.load(deps.storage)?.is_some() { + return Err(NeutronError::Std(StdError::generic_err( + "ICA channel already exists and open", + ))); + } + + INTERCHAIN_TX_IN_PROGRESS.save(deps.storage, &true)?; + + let config = CONFIG.load(deps.storage)?; + let register_ica = + NeutronMsg::register_interchain_account(config.connection_id, ICA_ID.to_string()); + + Ok(Response::default().add_message(register_ica)) +} + fn execute_claim_unclaimed( deps: DepsMut, _env: Env, + _info: MessageInfo, current_stage: Stage, ) -> NeutronResult> { if current_stage != Stage::ClaimUnclaimed { @@ -139,7 +157,9 @@ fn execute_send_claimed_tokens_to_ica( let config = CONFIG.load(deps.storage)?; let ica = INTERCHAIN_ACCOUNT.load(deps.storage)?.ok_or_else(|| { - NeutronError::Std(StdError::generic_err("no ica created yet".to_string())) + NeutronError::Std(StdError::generic_err( + "ica is not created or open".to_string(), + )) })?; let neutron_on_balance = deps .querier @@ -185,7 +205,9 @@ fn execute_fund_community_pool( INTERCHAIN_TX_IN_PROGRESS.save(deps.storage, &true)?; let config = CONFIG.load(deps.storage)?; let ica = INTERCHAIN_ACCOUNT.load(deps.storage)?.ok_or_else(|| { - NeutronError::Std(StdError::generic_err("no ica address yet".to_string())) + NeutronError::Std(StdError::generic_err( + "ica is not created or open".to_string(), + )) })?; let amount = CosmosCoin { @@ -274,30 +296,34 @@ pub fn sudo(deps: DepsMut, env: Env, msg: SudoMsg) -> StdResult { ), SudoMsg::Response { request, data } => sudo_response(deps, request, data), SudoMsg::Error { request, details } => sudo_error(deps, request, details), - SudoMsg::Timeout { request } => sudo_timeout(deps, request), + SudoMsg::Timeout { request } => sudo_timeout(deps, env, request), _ => Ok(Response::default()), } } -fn sudo_response(deps: DepsMut, _request: RequestPacket, data: Binary) -> StdResult { +fn sudo_response(deps: DepsMut, request: RequestPacket, data: Binary) -> StdResult { deps.api.debug("WASMDEBUG: sudo response"); INTERCHAIN_TX_IN_PROGRESS.save(deps.storage, &false)?; - let parsed_data = decode_acknowledgement_response(data)?; - for item in parsed_data { - let item_type = item.msg_type.as_str(); - deps.api - .debug(&format!("WASMDEBUG: item_type = {}", item_type)); - match item_type { - MSG_IBC_TRANSFER => { - STAGE.save(deps.storage, &Stage::FundCommunityPool)?; - } - MSG_FUND_COMMUNITY_POOL => { - STAGE.save(deps.storage, &Stage::Done)?; - } - _ => { - // TODO: what to do? - continue; + let source_port = request + .source_port + .ok_or_else(|| StdError::generic_err("source_port not found"))?; + let is_transfer = source_port == TRANSFER_PORT; + if is_transfer { + STAGE.save(deps.storage, &Stage::FundCommunityPool)?; + } else { + let parsed_data = decode_acknowledgement_response(data)?; + for item in parsed_data { + let item_type = item.msg_type.as_str(); + deps.api + .debug(&format!("WASMDEBUG: item_type = {}", item_type)); + match item_type { + MSG_FUND_COMMUNITY_POOL => { + STAGE.save(deps.storage, &Stage::Done)?; + } + _ => { + continue; + } } } } @@ -311,46 +337,28 @@ fn sudo_error(deps: DepsMut, _request: RequestPacket, _details: String) -> StdRe Ok(Response::default()) } -fn sudo_timeout(deps: DepsMut, _request: RequestPacket) -> StdResult { +// can be called by response of create ica, ibc transfer and fund community pool +fn sudo_timeout(deps: DepsMut, _env: Env, request: RequestPacket) -> StdResult { INTERCHAIN_TX_IN_PROGRESS.save(deps.storage, &false)?; - // TODO: if timeout happened on ICA -> ICA closes, we need to be able to reopen ica channel, - // but still be able to call sudo_timeout then - - // TODO: set opened to false if sent by ICA - - Ok(Response::default()) -} + let source_port = request + .source_port + .ok_or_else(|| StdError::generic_err("source_port not found"))?; -fn execute_create_hub_ica( - deps: DepsMut, - _env: Env, -) -> NeutronResult> { - let ica = INTERCHAIN_ACCOUNT.load(deps.storage)?; - // return if ICA channel exists and opened - match ica { - Some(InterchainAccount{ address: _, open: true }) => { - return Err(NeutronError::Std(StdError::generic_err(format!( - "ICA channel already exists and open", - )))); + // ICA transactions timeout closes the channel + if let Some(ica) = INTERCHAIN_ACCOUNT.load(deps.storage)? { + if source_port == ica.source_port_id { + INTERCHAIN_ACCOUNT.save(deps.storage, &None)?; } - _ => {}, } - INTERCHAIN_TX_IN_PROGRESS.save(deps.storage, &true)?; - - let config = CONFIG.load(deps.storage)?; - - let register_ica = NeutronMsg::register_interchain_account(config.connection_id, ICA_ID.to_string()); - - INTERCHAIN_ACCOUNT.save(deps.storage, &None)?; - Ok(Response::default().add_message(register_ica)) + Ok(Response::default()) } fn sudo_open_ack( deps: DepsMut, _env: Env, - _port_id: String, + port_id: String, _channel_id: String, _counterparty_channel_id: String, counterparty_version: String, @@ -362,7 +370,7 @@ fn sudo_open_ack( deps.storage, &Some(InterchainAccount { address: parsed_version.address, - open: true, + source_port_id: port_id, // TODO: is this the source port_id ? }), )?; STAGE.save(deps.storage, &Stage::SendClaimedTokensToICA)?; diff --git a/src/helpers.rs b/src/helpers.rs index cb2a4e6..a39b155 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,6 +1,5 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use std::fmt::Display; use cosmwasm_std::{to_binary, Addr, CosmosMsg, StdResult, WasmMsg}; diff --git a/src/msg.rs b/src/msg.rs index 990f6c4..834aa96 100644 --- a/src/msg.rs +++ b/src/msg.rs @@ -17,14 +17,14 @@ pub struct InstantiateMsg { #[cw_serde] pub enum ExecuteMsg { + /// Creates ICA. Can be called if ICA is not created or channel was closed. + CreateHubICA {}, /// Step 1. Claim unclaimed airdrops and send them to this contract. ClaimUnclaimed {}, /// Step 2. Requires ICA to be created. Send funds to ICA account. - SendClaimedTokensToICA { }, + SendClaimedTokensToICA {}, /// Step 3. Requires ICA to be created and open. Fund cosmoshub community pool with sent funds. - FundCommunityPool { }, - /// Creates ICA. Can be called if ICA does not created or channel was closed. - CreateHubICA { }, + FundCommunityPool {}, } #[cw_serde] diff --git a/src/state.rs b/src/state.rs index c44b9b6..6399e2c 100644 --- a/src/state.rs +++ b/src/state.rs @@ -33,10 +33,12 @@ pub struct Config { #[cw_serde] pub struct InterchainAccount { + // ica address on remote network pub address: String, - pub open: bool, + pub source_port_id: String, } +// TODO: can we import it from library? #[cw_serde] pub struct OpenAckVersion { pub version: String,