From 6a5079f5ad77b49b2a871c63ee11bb140ad7e12f Mon Sep 17 00:00:00 2001 From: ducphamle2 Date: Tue, 30 May 2023 00:24:13 -0700 Subject: [PATCH 1/5] fixed wrong evm prefix input process deduct fee in build_ibc_msg --- contracts/cw-ics20-latest/src/contract.rs | 6 +++--- contracts/cw-ics20-latest/src/ibc.rs | 12 ++++++++---- contracts/cw-ics20-latest/src/ibc_tests.rs | 8 +++++++- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/contracts/cw-ics20-latest/src/contract.rs b/contracts/cw-ics20-latest/src/contract.rs index 27dc709..1cb0d8c 100644 --- a/contracts/cw-ics20-latest/src/contract.rs +++ b/contracts/cw-ics20-latest/src/contract.rs @@ -11,8 +11,8 @@ use oraiswap::asset::AssetInfo; use crate::error::ContractError; use crate::ibc::{ - collect_transfer_fee_msgs, parse_ibc_wasm_port_id, parse_voucher_denom, process_deduct_fee, - Ics20Packet, + collect_transfer_fee_msgs, convert_remote_denom_to_evm_prefix, parse_ibc_wasm_port_id, + parse_voucher_denom, process_deduct_fee, Ics20Packet, }; use crate::msg::{ AllowMsg, AllowedInfo, AllowedResponse, ChannelResponse, ConfigResponse, DeletePairMsg, @@ -253,7 +253,7 @@ pub fn execute_transfer_back_to_remote_chain( let new_deducted_amount = process_deduct_fee( deps.storage, - &msg.remote_denom, + &convert_remote_denom_to_evm_prefix(&msg.remote_denom), amount.amount(), &amount.denom(), )?; diff --git a/contracts/cw-ics20-latest/src/ibc.rs b/contracts/cw-ics20-latest/src/ibc.rs index 4349d9d..c05d2a5 100644 --- a/contracts/cw-ics20-latest/src/ibc.rs +++ b/contracts/cw-ics20-latest/src/ibc.rs @@ -412,7 +412,12 @@ fn handle_ibc_packet_receive_native_remote_chain( let new_deducted_to_send = Amount::from_parts( to_send.denom(), - process_deduct_fee(storage, &msg.denom, to_send.amount(), &to_send.denom())?, + process_deduct_fee( + storage, + &convert_remote_denom_to_evm_prefix(&msg.denom), + to_send.amount(), + &to_send.denom(), + )?, ); // after receiving the cw20 amount, we try to do fee swapping for the user if needed so he / she can create txs on the network @@ -666,7 +671,7 @@ pub fn build_ibc_msg( // also deduct fee here because of round trip let new_deducted_amount = process_deduct_fee( storage, - &destination.destination_denom, + &destination.destination_channel, amount, &parse_asset_info_denom(receiver_asset_info.clone()), )?; @@ -793,11 +798,10 @@ pub fn check_gas_limit(deps: Deps, amount: &Amount) -> Result, Contr pub fn process_deduct_fee( storage: &mut dyn Storage, - remote_token_denom: &str, + evm_prefix: &str, amount: Uint128, local_token_denom: &str, ) -> StdResult { - let evm_prefix = convert_remote_denom_to_evm_prefix(remote_token_denom); let relayer_fee = RELAYER_FEE.may_load(storage, &evm_prefix)?; if let Some(relayer_fee) = relayer_fee { let fee = deduct_fee(relayer_fee, amount); diff --git a/contracts/cw-ics20-latest/src/ibc_tests.rs b/contracts/cw-ics20-latest/src/ibc_tests.rs index 8126988..52a7daf 100644 --- a/contracts/cw-ics20-latest/src/ibc_tests.rs +++ b/contracts/cw-ics20-latest/src/ibc_tests.rs @@ -1116,7 +1116,13 @@ mod test { ) .unwrap(); assert_eq!( - process_deduct_fee(storage, "foo0x", amount, "foo").unwrap(), + process_deduct_fee( + storage, + &convert_remote_denom_to_evm_prefix("foo0x"), + amount, + "foo" + ) + .unwrap(), Uint128::from(990u64) ); assert_eq!( From bb97fa90aee1b622190e18c59e12c8a3e3479498 Mon Sep 17 00:00:00 2001 From: ducphamle2 Date: Tue, 30 May 2023 00:35:13 -0700 Subject: [PATCH 2/5] added fee receiver intead of using admin contract --- contracts/cw-ics20-latest/src/contract.rs | 65 +++++++++++----------- contracts/cw-ics20-latest/src/ibc.rs | 18 ++---- contracts/cw-ics20-latest/src/ibc_tests.rs | 1 + contracts/cw-ics20-latest/src/msg.rs | 2 + contracts/cw-ics20-latest/src/state.rs | 1 + 5 files changed, 44 insertions(+), 43 deletions(-) diff --git a/contracts/cw-ics20-latest/src/contract.rs b/contracts/cw-ics20-latest/src/contract.rs index 1cb0d8c..cf3180c 100644 --- a/contracts/cw-ics20-latest/src/contract.rs +++ b/contracts/cw-ics20-latest/src/contract.rs @@ -2,7 +2,7 @@ use cosmwasm_std::entry_point; use cosmwasm_std::{ from_binary, to_binary, Addr, Binary, Deps, DepsMut, Empty, Env, IbcEndpoint, IbcMsg, IbcQuery, - MessageInfo, Order, PortIdResponse, Response, StdError, StdResult, + MessageInfo, Order, PortIdResponse, Response, StdResult, }; use cw2::set_contract_version; use cw20::{Cw20Coin, Cw20ReceiveMsg}; @@ -39,17 +39,17 @@ pub fn instantiate( msg: InitMsg, ) -> Result { set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + let admin = deps.api.addr_validate(&msg.gov_contract)?; + ADMIN.set(deps.branch(), Some(admin.clone()))?; let cfg = Config { default_timeout: msg.default_timeout, default_gas_limit: msg.default_gas_limit, fee_denom: "orai".to_string(), swap_router_contract: msg.swap_router_contract, + fee_receiver: admin, }; CONFIG.save(deps.storage, &cfg)?; - let admin = deps.api.addr_validate(&msg.gov_contract)?; - ADMIN.set(deps.branch(), Some(admin))?; - // add all allows for allowed in msg.allowlist { let contract = deps.api.addr_validate(&allowed.contract)?; @@ -89,6 +89,7 @@ pub fn execute( swap_router_contract, admin, relayer_fee, + fee_receiver, } => update_config( deps, info, @@ -98,6 +99,7 @@ pub fn execute( swap_router_contract, admin, relayer_fee, + fee_receiver, ), } } @@ -111,6 +113,7 @@ pub fn update_config( swap_router_contract: Option, admin: Option, relayer_fee: Option, + fee_receiver: Option, ) -> Result { ADMIN.assert_admin(deps.as_ref(), &info.sender)?; if let Some(relayer_fee) = relayer_fee { @@ -126,6 +129,9 @@ pub fn update_config( if let Some(swap_router_contract) = swap_router_contract { config.swap_router_contract = swap_router_contract; } + if let Some(fee_receiver) = fee_receiver { + config.fee_receiver = deps.api.addr_validate(&fee_receiver)?; + } config.default_gas_limit = default_gas_limit; Ok(config) })?; @@ -270,28 +276,27 @@ pub fn execute_transfer_back_to_remote_chain( )?; // parse denom & compare with user input. Should not use string.includes() because hacker can fake a port that has the same remote denom to return true - let mapping_search_result = mappings.into_iter().find(|pair| -> bool { - let (denom, is_native) = parse_voucher_denom( - pair.key.as_str(), - &IbcEndpoint { - port_id: parse_ibc_wasm_port_id(env.contract.address.clone().into_string()), - channel_id: msg.local_channel_id.clone(), // also verify local channel id - }, - ) - .unwrap_or_else(|_| ("", true)); // if there's an error, change is_native to true so it automatically returns false - if is_native { + let mapping = mappings + .into_iter() + .find(|pair| -> bool { + let (denom, is_native) = parse_voucher_denom( + pair.key.as_str(), + &IbcEndpoint { + port_id: parse_ibc_wasm_port_id(env.contract.address.clone().into_string()), + channel_id: msg.local_channel_id.clone(), // also verify local channel id + }, + ) + .unwrap_or_else(|_| ("", true)); // if there's an error, change is_native to true so it automatically returns false + if is_native { + return false; + } + if denom.eq(&msg.remote_denom) { + return true; + } return false; - } - if denom.eq(&msg.remote_denom) { - return true; - } - return false; - }); - if mapping_search_result.is_none() { - return Err(ContractError::MappingPairNotFound {}); - } + }) + .ok_or(ContractError::MappingPairNotFound {})?; - let mapping = mapping_search_result.unwrap(); let ibc_denom = mapping.key; // ensure the requested channel is registered if !CHANNEL_INFO.has(deps.storage, &msg.local_channel_id) { @@ -341,13 +346,8 @@ pub fn execute_transfer_back_to_remote_chain( timeout: timeout.into(), }; - let mut cosmos_msgs = collect_transfer_fee_msgs( - ADMIN - .get(deps.as_ref())? - .ok_or(StdError::generic_err("Cannot find contract admin"))? - .into_string(), - deps.storage, - )?; + let mut cosmos_msgs = + collect_transfer_fee_msgs(config.fee_receiver.into_string(), deps.storage)?; cosmos_msgs.push(msg.into()); // send response @@ -475,6 +475,7 @@ pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> Result Result { // make sure we have enough balance for this @@ -438,7 +432,8 @@ fn handle_ibc_packet_receive_native_remote_chain( .map(|msg| SubMsg::reply_on_error(msg, FOLLOW_UP_FAILURE_ID)) .collect(); - let transfer_fee_to_admin = collect_transfer_fee_msgs(admin, storage)?; + let transfer_fee_to_admin = + collect_transfer_fee_msgs(CONFIG.load(storage)?.fee_receiver.into_string(), storage)?; let mut res = IbcReceiveResponse::new() .set_ack(ack_success()) .add_messages(transfer_fee_to_admin) @@ -835,7 +830,7 @@ pub fn convert_remote_denom_to_evm_prefix(remote_denom: &str) -> String { } pub fn collect_transfer_fee_msgs( - admin: String, + receiver: String, storage: &mut dyn Storage, ) -> StdResult> { let cosmos_msgs = RELAYER_FEE_ACCUMULATOR @@ -857,10 +852,9 @@ pub fn collect_transfer_fee_msgs( }) .map(|data| { data.map(|fee_info| { - println!("fee info: {}", fee_info.1); send_amount( Amount::from_parts(fee_info.0, fee_info.1), - admin.clone(), + receiver.clone(), None, ) }) diff --git a/contracts/cw-ics20-latest/src/ibc_tests.rs b/contracts/cw-ics20-latest/src/ibc_tests.rs index 52a7daf..7f16203 100644 --- a/contracts/cw-ics20-latest/src/ibc_tests.rs +++ b/contracts/cw-ics20-latest/src/ibc_tests.rs @@ -317,6 +317,7 @@ mod test { mock_env(), MigrateMsg { default_gas_limit: Some(def_limit), + fee_receiver: "receiver".to_string(), // default_timeout: 100u64, // fee_denom: "orai".to_string(), // swap_router_contract: "foobar".to_string(), diff --git a/contracts/cw-ics20-latest/src/msg.rs b/contracts/cw-ics20-latest/src/msg.rs index aa83895..a2f52fa 100644 --- a/contracts/cw-ics20-latest/src/msg.rs +++ b/contracts/cw-ics20-latest/src/msg.rs @@ -31,6 +31,7 @@ pub struct AllowMsg { pub struct MigrateMsg { // pub default_timeout: u64, pub default_gas_limit: Option, + pub fee_receiver: String, // pub fee_denom: String, // pub swap_router_contract: String, } @@ -54,6 +55,7 @@ pub enum ExecuteMsg { fee_denom: Option, swap_router_contract: Option, relayer_fee: Option, + fee_receiver: Option, }, } diff --git a/contracts/cw-ics20-latest/src/state.rs b/contracts/cw-ics20-latest/src/state.rs index d002ab4..a01cfe6 100644 --- a/contracts/cw-ics20-latest/src/state.rs +++ b/contracts/cw-ics20-latest/src/state.rs @@ -72,6 +72,7 @@ pub struct Config { pub default_gas_limit: Option, pub fee_denom: String, pub swap_router_contract: String, + pub fee_receiver: Addr, } #[cw_serde] From 11f9279a13ffd69384a8f03ecfdc3592e5df527a Mon Sep 17 00:00:00 2001 From: ducphamle2 Date: Mon, 5 Jun 2023 18:33:47 -0700 Subject: [PATCH 3/5] changed relayer fee to token fee --- contracts/cw-ics20-latest/src/contract.rs | 42 +++++-------- contracts/cw-ics20-latest/src/ibc.rs | 72 ++++++++++++---------- contracts/cw-ics20-latest/src/ibc_tests.rs | 57 +++++++++-------- contracts/cw-ics20-latest/src/msg.rs | 6 +- contracts/cw-ics20-latest/src/state.rs | 8 +-- 5 files changed, 97 insertions(+), 88 deletions(-) diff --git a/contracts/cw-ics20-latest/src/contract.rs b/contracts/cw-ics20-latest/src/contract.rs index cf3180c..1cd931f 100644 --- a/contracts/cw-ics20-latest/src/contract.rs +++ b/contracts/cw-ics20-latest/src/contract.rs @@ -11,8 +11,8 @@ use oraiswap::asset::AssetInfo; use crate::error::ContractError; use crate::ibc::{ - collect_transfer_fee_msgs, convert_remote_denom_to_evm_prefix, parse_ibc_wasm_port_id, - parse_voucher_denom, process_deduct_fee, Ics20Packet, + collect_transfer_fee_msgs, parse_ibc_wasm_port_id, parse_voucher_denom, process_deduct_fee, + Ics20Packet, }; use crate::msg::{ AllowMsg, AllowedInfo, AllowedResponse, ChannelResponse, ConfigResponse, DeletePairMsg, @@ -21,8 +21,8 @@ use crate::msg::{ }; use crate::state::{ get_key_ics20_ibc_denom, ics20_denoms, increase_channel_balance, reduce_channel_balance, - AllowInfo, Config, MappingMetadata, RelayerFee, ADMIN, ALLOW_LIST, CHANNEL_FORWARD_STATE, - CHANNEL_INFO, CHANNEL_REVERSE_STATE, CONFIG, RELAYER_FEE, + AllowInfo, Config, MappingMetadata, TokenFee, ADMIN, ALLOW_LIST, CHANNEL_FORWARD_STATE, + CHANNEL_INFO, CHANNEL_REVERSE_STATE, CONFIG, TOKEN_FEE, }; use cw20_ics20_msg::amount::{convert_local_to_remote, Amount}; use cw_utils::{maybe_addr, nonpayable, one_coin}; @@ -88,7 +88,7 @@ pub fn execute( fee_denom, swap_router_contract, admin, - relayer_fee, + token_fee, fee_receiver, } => update_config( deps, @@ -98,7 +98,7 @@ pub fn execute( fee_denom, swap_router_contract, admin, - relayer_fee, + token_fee, fee_receiver, ), } @@ -112,12 +112,12 @@ pub fn update_config( fee_denom: Option, swap_router_contract: Option, admin: Option, - relayer_fee: Option, + token_fee: Option, fee_receiver: Option, ) -> Result { ADMIN.assert_admin(deps.as_ref(), &info.sender)?; - if let Some(relayer_fee) = relayer_fee { - RELAYER_FEE.save(deps.storage, &relayer_fee.chain, &relayer_fee.ratio)?; + if let Some(token_fee) = token_fee { + TOKEN_FEE.save(deps.storage, &token_fee.token_denom, &token_fee.ratio)?; } CONFIG.update(deps.storage, |mut config| -> StdResult { if let Some(default_timeout) = default_timeout { @@ -259,7 +259,7 @@ pub fn execute_transfer_back_to_remote_chain( let new_deducted_amount = process_deduct_fee( deps.storage, - &convert_remote_denom_to_evm_prefix(&msg.remote_denom), + &msg.remote_denom, amount.amount(), &amount.denom(), )?; @@ -505,8 +505,8 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { to_binary(&get_mappings_from_asset_info(deps, asset_info)?) } QueryMsg::Admin {} => to_binary(&ADMIN.query_admin(deps)?), - QueryMsg::GetTransferFee { evm_prefix } => { - to_binary(&RELAYER_FEE.load(deps.storage, &evm_prefix)?) + QueryMsg::GetTransferTokenFee { remote_token_denom } => { + to_binary(&TOKEN_FEE.load(deps.storage, &remote_token_denom)?) } } } @@ -681,8 +681,8 @@ mod test { use std::ops::Sub; use super::*; - use crate::ibc::{ibc_packet_receive, parse_asset_info_denom}; - use crate::state::{Ratio, RELAYER_FEE_ACCUMULATOR}; + use crate::ibc::ibc_packet_receive; + use crate::state::Ratio; use crate::test_helpers::*; use cosmwasm_std::testing::{mock_env, mock_info}; @@ -1186,16 +1186,8 @@ mod test { let fee_amount = Uint128::from(amount) * Decimal::from_ratio(ratio.nominator, ratio.denominator); let mut deps = setup(&[remote_channel, local_channel], &[]); - RELAYER_FEE - .save(deps.as_mut().storage, "uatom", &ratio) - .unwrap(); - // reset fee so that other tests wont get affected - RELAYER_FEE_ACCUMULATOR - .save( - deps.as_mut().storage, - &parse_asset_info_denom(asset_info.clone()), - &Uint128::zero(), - ) + TOKEN_FEE + .save(deps.as_mut().storage, denom, &ratio) .unwrap(); let pair = UpdatePairMsg { @@ -1362,7 +1354,7 @@ mod test { default_gas_limit: None, fee_denom: Some("hehe".to_string()), swap_router_contract: Some("new_router".to_string()), - relayer_fee: None, + token_fee: None, fee_receiver: None, }; // unauthorized case diff --git a/contracts/cw-ics20-latest/src/ibc.rs b/contracts/cw-ics20-latest/src/ibc.rs index 4961369..65e7ec0 100644 --- a/contracts/cw-ics20-latest/src/ibc.rs +++ b/contracts/cw-ics20-latest/src/ibc.rs @@ -18,7 +18,7 @@ use crate::state::{ get_key_ics20_ibc_denom, ics20_denoms, increase_channel_balance, reduce_channel_balance, undo_increase_channel_balance, undo_reduce_channel_balance, ChannelInfo, IbcSingleStepData, MappingMetadata, Ratio, ReplyArgs, SingleStepReplyArgs, ALLOW_LIST, CHANNEL_INFO, CONFIG, - RELAYER_FEE, RELAYER_FEE_ACCUMULATOR, REPLY_ARGS, SINGLE_STEP_REPLY_ARGS, + REPLY_ARGS, SINGLE_STEP_REPLY_ARGS, TOKEN_FEE, TOKEN_FEE_ACCUMULATOR, }; use cw20::{Cw20ExecuteMsg, Cw20QueryMsg, TokenInfoResponse}; use cw20_ics20_msg::amount::{convert_local_to_remote, convert_remote_to_local, Amount}; @@ -308,6 +308,19 @@ pub fn parse_voucher_denom<'a>( Ok((split_denom[2], false)) } +// Returns local denom if the denom is an encoded voucher from the expected endpoint +// Otherwise, error +pub fn parse_voucher_denom_without_sanity_checks<'a>(voucher_denom: &'a str) -> StdResult<&'a str> { + let split_denom: Vec<&str> = voucher_denom.splitn(3, '/').collect(); + + if split_denom.len() != 3 { + return Err(StdError::generic_err( + ContractError::NoForeignTokens {}.to_string(), + )); + } + Ok(split_denom[2]) +} + // this does the work of ibc_packet_receive, we wrap it to turn errors into acknowledgements fn do_ibc_packet_receive( deps: DepsMut, @@ -406,12 +419,7 @@ fn handle_ibc_packet_receive_native_remote_chain( let new_deducted_to_send = Amount::from_parts( to_send.denom(), - process_deduct_fee( - storage, - &convert_remote_denom_to_evm_prefix(&msg.denom), - to_send.amount(), - &to_send.denom(), - )?, + process_deduct_fee(storage, &msg.denom, to_send.amount(), &to_send.denom())?, ); // after receiving the cw20 amount, we try to do fee swapping for the user if needed so he / she can create txs on the network @@ -663,13 +671,6 @@ pub fn build_ibc_msg( }; let (is_evm_based, destination) = destination.is_receiver_evm_based(); if is_evm_based { - // also deduct fee here because of round trip - let new_deducted_amount = process_deduct_fee( - storage, - &destination.destination_channel, - amount, - &parse_asset_info_denom(receiver_asset_info.clone()), - )?; // use sender from ICS20Packet as receiver when transferring back let pair_mappings: Vec<(String, MappingMetadata)> = ics20_denoms() .idx @@ -682,6 +683,13 @@ pub fn build_ibc_msg( .into_iter() .find(|(key, _)| key.contains(&destination.destination_channel)) .ok_or(StdError::generic_err("cannot find pair mappings"))?; + // also deduct fee here because of round trip + let new_deducted_amount = process_deduct_fee( + storage, + parse_voucher_denom_without_sanity_checks(&mapping.0)?, + amount, + &parse_asset_info_denom(receiver_asset_info.clone()), + )?; let remote_amount = convert_local_to_remote( new_deducted_amount, mapping.1.remote_decimals, @@ -793,14 +801,14 @@ pub fn check_gas_limit(deps: Deps, amount: &Amount) -> Result, Contr pub fn process_deduct_fee( storage: &mut dyn Storage, - evm_prefix: &str, + remote_token_denom: &str, amount: Uint128, local_token_denom: &str, ) -> StdResult { - let relayer_fee = RELAYER_FEE.may_load(storage, &evm_prefix)?; - if let Some(relayer_fee) = relayer_fee { - let fee = deduct_fee(relayer_fee, amount); - RELAYER_FEE_ACCUMULATOR.update( + let token_fee = TOKEN_FEE.may_load(storage, &remote_token_denom)?; + if let Some(token_fee) = token_fee { + let fee = deduct_fee(token_fee, amount); + TOKEN_FEE_ACCUMULATOR.update( storage, local_token_denom, |prev_fee| -> StdResult { Ok(prev_fee.unwrap_or_default().checked_add(fee)?) }, @@ -811,29 +819,29 @@ pub fn process_deduct_fee( Ok(amount) } -pub fn deduct_fee(relayer_fee: Ratio, amount: Uint128) -> Uint128 { +pub fn deduct_fee(token_fee: Ratio, amount: Uint128) -> Uint128 { // ignore case where denominator is zero since we cannot divide with 0 - if relayer_fee.denominator == 0 { + if token_fee.denominator == 0 { return Uint128::from(0u64); } amount.mul(Decimal::from_ratio( - relayer_fee.nominator, - relayer_fee.denominator, + token_fee.nominator, + token_fee.denominator, )) } -pub fn convert_remote_denom_to_evm_prefix(remote_denom: &str) -> String { - match remote_denom.split_once("0x") { - Some((evm_prefix, _)) => return evm_prefix.to_string(), - None => "".to_string(), - } -} +// pub fn convert_remote_denom_to_evm_prefix(remote_denom: &str) -> String { +// match remote_denom.split_once("0x") { +// Some((evm_prefix, _)) => return evm_prefix.to_string(), +// None => "".to_string(), +// } +// } pub fn collect_transfer_fee_msgs( receiver: String, storage: &mut dyn Storage, ) -> StdResult> { - let cosmos_msgs = RELAYER_FEE_ACCUMULATOR + let cosmos_msgs = TOKEN_FEE_ACCUMULATOR .range(storage, None, None, Order::Ascending) .filter(|data| { if let Some(filter_result) = data @@ -861,11 +869,11 @@ pub fn collect_transfer_fee_msgs( }) .collect::>>(); // we reset all the accumulator keys to zero so that it wont accumulate more in the next txs. This action will be reverted if the fee payment txs fail. - RELAYER_FEE_ACCUMULATOR + TOKEN_FEE_ACCUMULATOR .keys(storage, None, None, Order::Ascending) .collect::, StdError>>()? .into_iter() - .for_each(|key| RELAYER_FEE_ACCUMULATOR.remove(storage, &key)); + .for_each(|key| TOKEN_FEE_ACCUMULATOR.remove(storage, &key)); cosmos_msgs } diff --git a/contracts/cw-ics20-latest/src/ibc_tests.rs b/contracts/cw-ics20-latest/src/ibc_tests.rs index 7f16203..6e1c6ce 100644 --- a/contracts/cw-ics20-latest/src/ibc_tests.rs +++ b/contracts/cw-ics20-latest/src/ibc_tests.rs @@ -6,10 +6,10 @@ mod test { use oraiswap::router::SwapOperation; use crate::ibc::{ - ack_fail, build_ibc_msg, build_swap_msgs, check_gas_limit, - convert_remote_denom_to_evm_prefix, deduct_fee, handle_follow_up_failure, - ibc_packet_receive, is_follow_up_msgs_only_send_amount, parse_voucher_denom, - process_deduct_fee, send_amount, Ics20Ack, Ics20Packet, RECEIVE_ID, REFUND_FAILURE_ID, + ack_fail, build_ibc_msg, build_swap_msgs, check_gas_limit, deduct_fee, + handle_follow_up_failure, ibc_packet_receive, is_follow_up_msgs_only_send_amount, + parse_voucher_denom, parse_voucher_denom_without_sanity_checks, process_deduct_fee, + send_amount, Ics20Ack, Ics20Packet, RECEIVE_ID, REFUND_FAILURE_ID, }; use crate::ibc::{build_swap_operations, get_follow_up_msgs}; use crate::test_helpers::*; @@ -21,8 +21,8 @@ mod test { use crate::error::ContractError; use crate::state::{ get_key_ics20_ibc_denom, increase_channel_balance, ChannelState, IbcSingleStepData, Ratio, - SingleStepReplyArgs, CHANNEL_REVERSE_STATE, RELAYER_FEE, RELAYER_FEE_ACCUMULATOR, - SINGLE_STEP_REPLY_ARGS, + SingleStepReplyArgs, CHANNEL_REVERSE_STATE, SINGLE_STEP_REPLY_ARGS, TOKEN_FEE, + TOKEN_FEE_ACCUMULATOR, }; use cw20::{Cw20Coin, Cw20ExecuteMsg}; use cw20_ics20_msg::amount::{convert_local_to_remote, Amount}; @@ -343,7 +343,7 @@ mod test { ) -> IbcPacket { let data = Ics20Packet { // this is returning a foreign native token, thus denom is , eg: uatom - denom: format!("{}", denom), + denom: denom.to_string(), amount: amount.into(), sender: "remote-sender".to_string(), receiver: receiver.to_string(), @@ -466,10 +466,10 @@ mod test { &["channel-1", "channel-7", send_channel], &[(cw20_addr, gas_limit)], ); - RELAYER_FEE + TOKEN_FEE .save( deps.as_mut().storage, - "uatom", + denom, &Ratio { nominator: 1, denominator: 10, @@ -1086,14 +1086,28 @@ mod test { ); } + // #[test] + // fn test_convert_remote_denom_to_evm_prefix() { + // assert_eq!(convert_remote_denom_to_evm_prefix("abcd"), "".to_string()); + // assert_eq!(convert_remote_denom_to_evm_prefix("0x"), "".to_string()); + // assert_eq!( + // convert_remote_denom_to_evm_prefix("evm0x"), + // "evm".to_string() + // ); + // } + #[test] - fn test_convert_remote_denom_to_evm_prefix() { - assert_eq!(convert_remote_denom_to_evm_prefix("abcd"), "".to_string()); - assert_eq!(convert_remote_denom_to_evm_prefix("0x"), "".to_string()); + fn test_parse_voucher_denom_without_sanity_checks() { + assert_eq!( + parse_voucher_denom_without_sanity_checks("foo").is_err(), + true + ); assert_eq!( - convert_remote_denom_to_evm_prefix("evm0x"), - "evm".to_string() + parse_voucher_denom_without_sanity_checks("foo/bar").is_err(), + true ); + let result = parse_voucher_denom_without_sanity_checks("foo/bar/helloworld").unwrap(); + assert_eq!(result, "helloworld"); } #[test] @@ -1101,15 +1115,16 @@ mod test { let mut deps = mock_dependencies(); let amount = Uint128::from(1000u64); let storage = deps.as_mut().storage; + let token_fee_denom = "foo0x"; // should return amount because we have not set relayer fee yet assert_eq!( process_deduct_fee(storage, "foo", amount, "foo").unwrap(), amount.clone() ); - RELAYER_FEE + TOKEN_FEE .save( storage, - "foo", + token_fee_denom, &Ratio { nominator: 1, denominator: 100, @@ -1117,17 +1132,11 @@ mod test { ) .unwrap(); assert_eq!( - process_deduct_fee( - storage, - &convert_remote_denom_to_evm_prefix("foo0x"), - amount, - "foo" - ) - .unwrap(), + process_deduct_fee(storage, token_fee_denom, amount, "foo").unwrap(), Uint128::from(990u64) ); assert_eq!( - RELAYER_FEE_ACCUMULATOR.load(storage, "foo").unwrap(), + TOKEN_FEE_ACCUMULATOR.load(storage, "foo").unwrap(), Uint128::from(10u64) ); } diff --git a/contracts/cw-ics20-latest/src/msg.rs b/contracts/cw-ics20-latest/src/msg.rs index a2f52fa..09f30f8 100644 --- a/contracts/cw-ics20-latest/src/msg.rs +++ b/contracts/cw-ics20-latest/src/msg.rs @@ -3,7 +3,7 @@ use cosmwasm_std::{Binary, IbcEndpoint}; use cw20::Cw20ReceiveMsg; use oraiswap::asset::AssetInfo; -use crate::state::{ChannelInfo, MappingMetadata, Ratio, RelayerFee}; +use crate::state::{ChannelInfo, MappingMetadata, Ratio, TokenFee}; use cw20_ics20_msg::amount::Amount; #[cw_serde] @@ -54,7 +54,7 @@ pub enum ExecuteMsg { default_gas_limit: Option, fee_denom: Option, swap_router_contract: Option, - relayer_fee: Option, + token_fee: Option, fee_receiver: Option, }, } @@ -159,7 +159,7 @@ pub enum QueryMsg { #[returns(Vec)] PairMappingsFromAssetInfo { asset_info: AssetInfo }, #[returns(Ratio)] - GetTransferFee { evm_prefix: String }, + GetTransferTokenFee { remote_token_denom: String }, } #[cw_serde] diff --git a/contracts/cw-ics20-latest/src/state.rs b/contracts/cw-ics20-latest/src/state.rs index a01cfe6..ffc67ce 100644 --- a/contracts/cw-ics20-latest/src/state.rs +++ b/contracts/cw-ics20-latest/src/state.rs @@ -29,9 +29,9 @@ pub const CHANNEL_REVERSE_STATE: Map<(&str, &str), ChannelState> = /// Every cw20 contract we allow to be sent is stored here, possibly with a gas_limit pub const ALLOW_LIST: Map<&Addr, AllowInfo> = Map::new("allow_list"); -pub const RELAYER_FEE: Map<&str, Ratio> = Map::new("relayer_fee"); +pub const TOKEN_FEE: Map<&str, Ratio> = Map::new("token_fee"); -pub const RELAYER_FEE_ACCUMULATOR: Map<&str, Uint128> = Map::new("fee_accumulator"); +pub const TOKEN_FEE_ACCUMULATOR: Map<&str, Uint128> = Map::new("token_fee_accumulator"); // MappingMetadataIndexex structs keeps a list of indexers pub struct MappingMetadataIndexex<'a> { @@ -91,8 +91,8 @@ pub struct AllowInfo { } #[cw_serde] -pub struct RelayerFee { - pub chain: String, +pub struct TokenFee { + pub token_denom: String, pub ratio: Ratio, } From 5888017bd29bec0ed07a2402cb579ba7314ebbcf Mon Sep 17 00:00:00 2001 From: ducphamle2 Date: Mon, 5 Jun 2023 19:01:45 -0700 Subject: [PATCH 4/5] simplify build swap ops --- contracts/cw-ics20-latest/src/ibc.rs | 4 +--- contracts/cw-ics20-latest/src/ibc_tests.rs | 28 ++++++++-------------- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/contracts/cw-ics20-latest/src/ibc.rs b/contracts/cw-ics20-latest/src/ibc.rs index 65e7ec0..055aefd 100644 --- a/contracts/cw-ics20-latest/src/ibc.rs +++ b/contracts/cw-ics20-latest/src/ibc.rs @@ -500,7 +500,6 @@ pub fn get_follow_up_msgs( receiver_asset_info.clone(), initial_receive_asset_info.clone(), config.fee_denom.as_str(), - &destination.destination_denom, ); let mut minimum_receive = to_send.amount(); if swap_operations.len() > 0 { @@ -570,7 +569,6 @@ pub fn build_swap_operations( receiver_asset_info: AssetInfo, initial_receive_asset_info: AssetInfo, fee_denom: &str, - destination_denom: &str, ) -> Vec { // always swap with orai first cuz its base denom & every token has a pair with it let fee_denom_asset_info = AssetInfo::NativeToken { @@ -586,7 +584,7 @@ pub fn build_swap_operations( ask_asset_info: fee_denom_asset_info.clone(), }) } - if destination_denom.ne(fee_denom) { + if receiver_asset_info.to_string().ne(fee_denom) { swap_operations.push(SwapOperation::OraiSwap { offer_asset_info: fee_denom_asset_info.clone(), ask_asset_info: receiver_asset_info, diff --git a/contracts/cw-ics20-latest/src/ibc_tests.rs b/contracts/cw-ics20-latest/src/ibc_tests.rs index 6e1c6ce..4d002c6 100644 --- a/contracts/cw-ics20-latest/src/ibc_tests.rs +++ b/contracts/cw-ics20-latest/src/ibc_tests.rs @@ -555,26 +555,19 @@ mod test { contract_addr: Addr::unchecked("addr"), }; let fee_denom = "orai".to_string(); - let mut destination: DestinationInfo = DestinationInfo { - receiver: "cosmos".to_string(), - destination_channel: "channel-1".to_string(), - destination_denom: "foobar".to_string(), - }; let operations = build_swap_operations( receiver_asset_info.clone(), initial_asset_info.clone(), fee_denom.as_str(), - &destination.destination_denom, ); assert_eq!(operations.len(), 2); - let fee_denom = "foobar".to_string(); + let fee_denom = "contract".to_string(); let operations = build_swap_operations( receiver_asset_info.clone(), initial_asset_info.clone(), &fee_denom, - &destination.destination_denom, ); assert_eq!(operations.len(), 1); assert_eq!( @@ -587,31 +580,29 @@ mod test { } ); initial_asset_info = AssetInfo::NativeToken { - denom: "foobar".to_string(), + denom: "contract".to_string(), }; let operations = build_swap_operations( receiver_asset_info.clone(), initial_asset_info.clone(), &fee_denom, - &destination.destination_denom, ); assert_eq!(operations.len(), 0); - destination.destination_denom = "atom".to_string(); + initial_asset_info = AssetInfo::Token { + contract_addr: Addr::unchecked("addr"), + }; let operations = build_swap_operations( receiver_asset_info.clone(), initial_asset_info.clone(), &fee_denom, - &destination.destination_denom, ); assert_eq!(operations.len(), 1); assert_eq!( operations[0], SwapOperation::OraiSwap { - offer_asset_info: AssetInfo::NativeToken { - denom: fee_denom.clone() - }, - ask_asset_info: receiver_asset_info.clone() + offer_asset_info: initial_asset_info.clone(), + ask_asset_info: AssetInfo::NativeToken { denom: fee_denom } } ); @@ -620,9 +611,10 @@ mod test { AssetInfo::NativeToken { denom: "foobar".to_string(), }, - initial_asset_info.clone(), + AssetInfo::NativeToken { + denom: "foobar".to_string(), + }, "not_foo_bar", - &destination.destination_denom, ); assert_eq!(operations.len(), 0); } From bdb784837624086590909023d3f2c871e79f41f7 Mon Sep 17 00:00:00 2001 From: ducphamle2 Date: Tue, 6 Jun 2023 22:24:22 -0700 Subject: [PATCH 5/5] allowed updating many token fees at once --- contracts/cw-ics20-latest/src/contract.rs | 43 +++++++++++++++++++++-- contracts/cw-ics20-latest/src/msg.rs | 2 +- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/contracts/cw-ics20-latest/src/contract.rs b/contracts/cw-ics20-latest/src/contract.rs index 1cd931f..1e1c393 100644 --- a/contracts/cw-ics20-latest/src/contract.rs +++ b/contracts/cw-ics20-latest/src/contract.rs @@ -112,12 +112,14 @@ pub fn update_config( fee_denom: Option, swap_router_contract: Option, admin: Option, - token_fee: Option, + token_fee: Option>, fee_receiver: Option, ) -> Result { ADMIN.assert_admin(deps.as_ref(), &info.sender)?; if let Some(token_fee) = token_fee { - TOKEN_FEE.save(deps.storage, &token_fee.token_denom, &token_fee.ratio)?; + for fee in token_fee { + TOKEN_FEE.save(deps.storage, &fee.token_denom, &fee.ratio)?; + } } CONFIG.update(deps.storage, |mut config| -> StdResult { if let Some(default_timeout) = default_timeout { @@ -1354,7 +1356,22 @@ mod test { default_gas_limit: None, fee_denom: Some("hehe".to_string()), swap_router_contract: Some("new_router".to_string()), - token_fee: None, + token_fee: Some(vec![ + TokenFee { + token_denom: "orai".to_string(), + ratio: Ratio { + nominator: 1, + denominator: 10, + }, + }, + TokenFee { + token_denom: "atom".to_string(), + ratio: Ratio { + nominator: 1, + denominator: 5, + }, + }, + ]), fee_receiver: None, }; // unauthorized case @@ -1376,6 +1393,26 @@ mod test { assert_eq!(config.default_timeout, 1); assert_eq!(config.fee_denom, "hehe".to_string()); assert_eq!(config.swap_router_contract, "new_router".to_string()); + assert_eq!( + TOKEN_FEE + .range(deps.as_ref().storage, None, None, Order::Ascending) + .count(), + 2usize + ); + assert_eq!( + TOKEN_FEE + .load(deps.as_ref().storage, "orai") + .unwrap() + .denominator, + 10 + ); + assert_eq!( + TOKEN_FEE + .load(deps.as_ref().storage, "atom") + .unwrap() + .denominator, + 5 + ) } #[test] diff --git a/contracts/cw-ics20-latest/src/msg.rs b/contracts/cw-ics20-latest/src/msg.rs index 09f30f8..0201098 100644 --- a/contracts/cw-ics20-latest/src/msg.rs +++ b/contracts/cw-ics20-latest/src/msg.rs @@ -54,7 +54,7 @@ pub enum ExecuteMsg { default_gas_limit: Option, fee_denom: Option, swap_router_contract: Option, - token_fee: Option, + token_fee: Option>, fee_receiver: Option, }, }