Skip to content

Commit e053a46

Browse files
committed
fixed single-step refund using shared amount bug
1 parent 38f0213 commit e053a46

File tree

3 files changed

+56
-31
lines changed

3 files changed

+56
-31
lines changed

contracts/cw-ics20-latest/src/ibc.rs

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ use oraiswap::router::{SimulateSwapOperationsResponse, SwapOperation};
1313
use crate::error::{ContractError, Never};
1414
use crate::state::{
1515
get_key_ics20_ibc_denom, ics20_denoms, increase_channel_balance, reduce_channel_balance,
16-
undo_increase_channel_balance, undo_reduce_channel_balance, ChannelInfo, MappingMetadata,
17-
ReplyArgs, SingleStepReplyArgs, ALLOW_LIST, CHANNEL_INFO, CONFIG, REPLY_ARGS,
16+
undo_increase_channel_balance, undo_reduce_channel_balance, ChannelInfo, IbcSingleStepData,
17+
MappingMetadata, ReplyArgs, SingleStepReplyArgs, ALLOW_LIST, CHANNEL_INFO, CONFIG, REPLY_ARGS,
1818
SINGLE_STEP_REPLY_ARGS,
1919
};
2020
use cw20::{Cw20ExecuteMsg, Cw20QueryMsg, TokenInfoResponse};
@@ -618,9 +618,9 @@ pub fn build_ibc_msg(
618618
let mut reply_args = SingleStepReplyArgs {
619619
channel: destination.destination_channel.clone(),
620620
refund_asset_info: receiver_asset_info.clone(),
621-
ibc_denom: None,
621+
ibc_data: None,
622622
receiver: local_receiver.to_string(),
623-
amount,
623+
local_amount: amount,
624624
};
625625
let (is_evm_based, destination) = destination.is_receiver_evm_based();
626626
if is_evm_based {
@@ -642,15 +642,15 @@ pub fn build_ibc_msg(
642642
.into_iter()
643643
.find(|(key, _)| key.contains(&destination.destination_channel));
644644
if let Some(pair_mapping) = pair_mappings {
645-
let amount = convert_local_to_remote(
645+
let remote_amount = convert_local_to_remote(
646646
amount,
647647
pair_mapping.1.remote_decimals,
648648
pair_mapping.1.asset_info_decimals,
649649
)?;
650650

651651
// build ics20 packet
652652
let packet = Ics20Packet::new(
653-
amount.clone(),
653+
remote_amount.clone(),
654654
pair_mapping.0.clone(), // we use ibc denom in form <transfer>/<channel>/<denom> so that when it is sent back to remote chain, it gets parsed correctly and burned
655655
env.contract.address.as_str(),
656656
&remote_address,
@@ -661,12 +661,15 @@ pub fn build_ibc_msg(
661661
storage,
662662
&local_channel_id.clone(),
663663
&pair_mapping.0.clone(),
664-
amount,
664+
remote_amount,
665665
false,
666666
)
667667
.map_err(|err| StdError::generic_err(err.to_string()))?;
668668
reply_args.channel = local_channel_id.to_string();
669-
reply_args.ibc_denom = Some(pair_mapping.0);
669+
reply_args.ibc_data = Some(IbcSingleStepData {
670+
ibc_denom: pair_mapping.0,
671+
remote_amount,
672+
});
670673
// keep track of the reply. We need to keep a seperate value because if using REPLY, it could be overriden by the channel increase later on
671674
SINGLE_STEP_REPLY_ARGS.save(storage, &reply_args)?;
672675

@@ -697,22 +700,27 @@ pub fn handle_follow_up_failure(
697700
err: String,
698701
) -> Result<Response, ContractError> {
699702
// if there's an error but no ibc msg aka no channel balance reduce => wont undo reduce
700-
if let Some(ibc_denom) = reply_args.ibc_denom {
703+
let mut response: Response = Response::new();
704+
if let Some(ibc_data) = reply_args.ibc_data {
701705
undo_reduce_channel_balance(
702706
storage,
703707
&reply_args.channel,
704-
&ibc_denom,
705-
reply_args.amount,
708+
&ibc_data.ibc_denom,
709+
ibc_data.remote_amount,
706710
false,
707711
)?;
712+
response = response.add_attributes(vec![
713+
attr("ibc_denom_undo_channel_balance", ibc_data.ibc_denom),
714+
attr("remote_amount_undo_channel_balance", ibc_data.remote_amount),
715+
]);
708716
}
709717
let refund_amount = Amount::from_parts(
710718
parse_asset_info_denom(reply_args.refund_asset_info.clone()),
711-
reply_args.amount,
719+
reply_args.local_amount,
712720
);
713721
// we send refund to the local receiver of the single-step tx because the funds are currently in this contract
714722
let send = send_amount(refund_amount, reply_args.receiver, None);
715-
Ok(Response::new()
723+
response = response
716724
.add_submessage(SubMsg::reply_on_error(send, REFUND_FAILURE_ID))
717725
.set_data(ack_fail(err.clone()))
718726
.add_attributes(vec![
@@ -721,8 +729,9 @@ pub fn handle_follow_up_failure(
721729
"attempt_refund_denom",
722730
reply_args.refund_asset_info.to_string(),
723731
),
724-
attr("attempt_refund_amount", reply_args.amount),
725-
]))
732+
attr("attempt_refund_amount", reply_args.local_amount),
733+
]);
734+
Ok(response)
726735
}
727736

728737
pub fn check_gas_limit(deps: Deps, amount: &Amount) -> Result<Option<u64>, ContractError> {

contracts/cw-ics20-latest/src/ibc_tests.rs

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ mod test {
1919

2020
use crate::error::ContractError;
2121
use crate::state::{
22-
get_key_ics20_ibc_denom, increase_channel_balance, ChannelState, SingleStepReplyArgs,
23-
CHANNEL_REVERSE_STATE, SINGLE_STEP_REPLY_ARGS,
22+
get_key_ics20_ibc_denom, increase_channel_balance, ChannelState, IbcSingleStepData,
23+
SingleStepReplyArgs, CHANNEL_REVERSE_STATE, SINGLE_STEP_REPLY_ARGS,
2424
};
2525
use cw20::{Cw20Coin, Cw20ExecuteMsg};
26-
use cw20_ics20_msg::amount::Amount;
26+
use cw20_ics20_msg::amount::{convert_local_to_remote, Amount};
2727

2828
use crate::contract::{execute, migrate, query_channel};
2929
use crate::msg::{ExecuteMsg, MigrateMsg, TransferMsg, UpdatePairMsg};
@@ -638,6 +638,10 @@ mod test {
638638
denom: "orai".to_string(),
639639
};
640640
let amount = Uint128::from(10u128);
641+
let remote_decimals = 18;
642+
let asset_info_decimals = 6;
643+
let remote_amount =
644+
convert_local_to_remote(amount, remote_decimals, asset_info_decimals).unwrap();
641645
let remote_address = "eth-mainnet0x1235";
642646
let mut env = mock_env();
643647
env.contract.address = Addr::unchecked("addr");
@@ -714,8 +718,8 @@ mod test {
714718
local_channel_id: "mars-channel".to_string(),
715719
denom: "trx-mainnet".to_string(),
716720
asset_info: receiver_asset_info.clone(),
717-
remote_decimals: 18,
718-
asset_info_decimals: 18,
721+
remote_decimals,
722+
asset_info_decimals,
719723
};
720724

721725
// works with proper funds
@@ -731,7 +735,7 @@ mod test {
731735
deps.as_mut().storage,
732736
receive_channel,
733737
pair_mapping_key.as_str(),
734-
amount.clone(),
738+
remote_amount.clone(),
735739
false,
736740
)
737741
.unwrap();
@@ -755,7 +759,7 @@ mod test {
755759
CosmosMsg::Ibc(IbcMsg::SendPacket {
756760
channel_id: receive_channel.to_string(),
757761
data: to_binary(&Ics20Packet::new(
758-
amount.clone(),
762+
remote_amount.clone(),
759763
pair_mapping_key.clone(),
760764
env.contract.address.as_str(),
761765
&remote_address,
@@ -766,9 +770,11 @@ mod test {
766770
})
767771
);
768772
let reply_args = SINGLE_STEP_REPLY_ARGS.load(deps.as_mut().storage).unwrap();
769-
assert_eq!(reply_args.amount, amount);
773+
let ibc_data = reply_args.ibc_data.unwrap();
774+
assert_eq!(ibc_data.remote_amount, remote_amount);
775+
assert_eq!(reply_args.local_amount, amount);
770776
assert_eq!(reply_args.channel, receive_channel);
771-
assert_eq!(reply_args.ibc_denom, Some(pair_mapping_key));
777+
assert_eq!(ibc_data.ibc_denom, pair_mapping_key);
772778
assert_eq!(reply_args.receiver, local_receiver.to_string());
773779
assert_eq!(reply_args.refund_asset_info, receiver_asset_info)
774780
}
@@ -866,8 +872,8 @@ mod test {
866872
let mut single_step_reply_args = SingleStepReplyArgs {
867873
channel: local_channel_id.to_string(),
868874
refund_asset_info: refund_asset_info.clone(),
869-
ibc_denom: None,
870-
amount: amount.clone(),
875+
ibc_data: None,
876+
local_amount: amount,
871877
receiver: receiver.to_string(),
872878
};
873879
let result = handle_follow_up_failure(
@@ -894,12 +900,16 @@ mod test {
894900
"attempt_refund_denom",
895901
single_step_reply_args.refund_asset_info.to_string(),
896902
),
897-
attr("attempt_refund_amount", single_step_reply_args.amount),
903+
attr("attempt_refund_amount", single_step_reply_args.local_amount),
898904
])
899905
);
900906

901907
let ibc_denom = "ibc_denom";
902-
single_step_reply_args.ibc_denom = Some(ibc_denom.to_string());
908+
let remote_amount = convert_local_to_remote(amount, 18, 6).unwrap();
909+
single_step_reply_args.ibc_data = Some(IbcSingleStepData {
910+
ibc_denom: ibc_denom.to_string(),
911+
remote_amount: remote_amount.clone(),
912+
});
903913
// if has ibc denom then it's evm based, need to undo reducing balance
904914
CHANNEL_REVERSE_STATE
905915
.save(
@@ -921,6 +931,6 @@ mod test {
921931
.load(deps.as_mut().storage, (local_channel_id, ibc_denom))
922932
.unwrap();
923933
// should undo reduce channel state
924-
assert_eq!(channel_state.outstanding, amount)
934+
assert_eq!(channel_state.outstanding, remote_amount)
925935
}
926936
}

contracts/cw-ics20-latest/src/state.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,18 @@ pub struct ReplyArgs {
9999
pub amount: Uint128,
100100
}
101101

102+
#[cw_serde]
103+
pub struct IbcSingleStepData {
104+
pub ibc_denom: String,
105+
pub remote_amount: Uint128,
106+
}
107+
102108
#[cw_serde]
103109
pub struct SingleStepReplyArgs {
104110
pub channel: String,
105111
pub refund_asset_info: AssetInfo,
106-
pub ibc_denom: Option<String>,
107-
pub amount: Uint128,
112+
pub ibc_data: Option<IbcSingleStepData>,
113+
pub local_amount: Uint128,
108114
pub receiver: String,
109115
}
110116

0 commit comments

Comments
 (0)