Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions crates/ibc/src/context/token_transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ where
pub(crate) inner: Rc<RefCell<C>>,
pub(crate) verifiers: Rc<RefCell<BTreeSet<Address>>>,
is_shielded: bool,
is_refund: bool,
}

impl<C> TokenTransferContext<C>
Expand All @@ -42,6 +43,7 @@ where
inner,
verifiers,
is_shielded: false,
is_refund: false,
}
}

Expand All @@ -55,6 +57,11 @@ where
self.is_shielded = true;
}

/// Set the transfer as refund
pub fn enable_refund_transfer(&mut self) {
self.is_refund = true;
}

fn validate_sent_coin(&self, coin: &PrefixedCoin) -> Result<(), HostError> {
// The base denom should not be an IBC token address because an IBC
// token address has been already encoded and other chains can't extract
Expand Down Expand Up @@ -312,7 +319,9 @@ where
) -> Result<(), HostError> {
let (ibc_token, amount) = self.get_token_amount(coin)?;

self.add_deposit(&ibc_token, amount)?;
if !self.is_refund {
self.add_deposit(&ibc_token, amount)?;
}

self.inner
.borrow_mut()
Expand All @@ -329,7 +338,9 @@ where
let (ibc_token, amount) = self.get_token_amount(coin)?;

self.update_mint_amount(&ibc_token, amount, true)?;
self.add_deposit(&ibc_token, amount)?;
if !self.is_refund {
self.add_deposit(&ibc_token, amount)?;
}

// A transfer of NUT tokens must be verified by their VP
if ibc_token.is_internal()
Expand Down
2 changes: 2 additions & 0 deletions crates/ibc/src/context/transfer_mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ where
acknowledgement: &Acknowledgement,
relayer: &Signer,
) -> (ModuleExtras, Result<(), ChannelError>) {
self.ctx.enable_refund_transfer();
let (extras, result) = on_acknowledgement_packet_execute(
&mut self.ctx,
packet,
Expand All @@ -325,6 +326,7 @@ where
packet: &Packet,
relayer: &Signer,
) -> (ModuleExtras, Result<(), ChannelError>) {
self.ctx.enable_refund_transfer();
let (extras, result) =
on_timeout_packet_execute(&mut self.ctx, packet, relayer);
(extras, result.map_err(into_channel_error))
Expand Down
16 changes: 0 additions & 16 deletions crates/ibc/src/vp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2909,16 +2909,8 @@ mod tests {
.delete(&commitment_key)
.expect("delete failed");
keys_changed.insert(commitment_key);
// deposit
let data = serde_json::from_slice::<PacketData>(&packet.data)
.expect("decoding packet data failed");
let deposit_key = deposit_key(&nam());
let bytes = amount.serialize_to_vec();
let _ = state
.write_log_mut()
.write(&deposit_key, bytes)
.expect("write failed");
keys_changed.insert(deposit_key);
// event
let timeout_event = TimeoutEvent {
refund_receiver: data.sender,
Expand Down Expand Up @@ -3068,16 +3060,8 @@ mod tests {
.delete(&commitment_key)
.expect("delete failed");
keys_changed.insert(commitment_key);
// deposit
let data = serde_json::from_slice::<PacketData>(&packet.data)
.expect("decoding packet data failed");
let deposit_key = deposit_key(&nam());
let bytes = amount.serialize_to_vec();
let _ = state
.write_log_mut()
.write(&deposit_key, bytes)
.expect("write failed");
keys_changed.insert(deposit_key);
// event
let timeout_event = TimeoutEvent {
refund_receiver: data.sender,
Expand Down
28 changes: 28 additions & 0 deletions crates/tests/src/e2e/ibc_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1073,6 +1073,7 @@ fn ibc_upgrade_client() -> Result<()> {
/// - Transfer 1 NAM in an epoch will succeed
/// - Transfer 1 NAM in the same epoch will fail
/// - Transfer 1 NAM in the next epoch will succeed
/// - Refund will succeed after sending 1 NAM
/// 2. Test the mint limit
/// - The mint limit is 1
/// - Receiving 2 samoleans from Gaia will fail
Expand Down Expand Up @@ -1233,6 +1234,33 @@ fn ibc_rate_limit() -> Result<()> {
epoch = get_epoch(&test, &rpc).unwrap();
}

// Send 1 Apfel from Namada to an invalid Gaia address to trigger a refund
transfer(
&test,
ALBERT,
"invalid_receiver",
APFEL,
1,
Some(ALBERT_KEY),
&port_id_namada,
&channel_id_namada,
None,
None,
None,
None,
false,
None,
None,
)?;
// the per-epoch limit has reached but the refund should succeed
wait_for_packet_relay(
&hermes_dir,
&port_id_namada,
&channel_id_namada,
&test,
)?;
check_balance(&test, ALBERT, APFEL, 1_000_000)?;

// Transfer 2 samoleans from Gaia to Namada will succeed, but Namada can't
// receive due to the mint limit and the packet will be timed out
let namada_receiver = find_address(&test, ALBERT)?.to_string();
Expand Down
Loading