diff --git a/modules/apps/transfer/keeper/relay.go b/modules/apps/transfer/keeper/relay.go index 6a7c6b140df..284ab4ca83f 100644 --- a/modules/apps/transfer/keeper/relay.go +++ b/modules/apps/transfer/keeper/relay.go @@ -101,20 +101,12 @@ func (k Keeper) sendTransfer( if types.SenderChainIsSource(sourcePort, sourceChannel, fullDenomPath) { labels = append(labels, telemetry.NewLabel(coretypes.LabelSource, "true")) - // create the escrow address for the tokens + // obtain the escrow address for the source channel end escrowAddress := types.GetEscrowAddress(sourcePort, sourceChannel) - - // escrow source tokens. It fails if balance insufficient. - if err := k.bankKeeper.SendCoins( - ctx, sender, escrowAddress, sdk.NewCoins(token), - ); err != nil { + if err := k.escrowToken(ctx, sender, escrowAddress, token); err != nil { return 0, err } - // track the total amount in escrow keyed by denomination to allow for efficient iteration - currentTotalEscrow := k.GetTotalEscrowForDenom(ctx, token.GetDenom()) - newTotalEscrow := currentTotalEscrow.Add(token.Amount) - k.SetTotalEscrowForDenom(ctx, token.GetDenom(), newTotalEscrow) } else { labels = append(labels, telemetry.NewLabel(coretypes.LabelSource, "false")) @@ -224,21 +216,11 @@ func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, data t return errorsmod.Wrapf(ibcerrors.ErrUnauthorized, "%s is not allowed to receive funds", receiver) } - // unescrow tokens escrowAddress := types.GetEscrowAddress(packet.GetDestPort(), packet.GetDestChannel()) - if err := k.bankKeeper.SendCoins(ctx, escrowAddress, receiver, sdk.NewCoins(token)); err != nil { - // NOTE: this error is only expected to occur given an unexpected bug or a malicious - // counterparty module. The bug may occur in bank or any part of the code that allows - // the escrow address to be drained. A malicious counterparty module could drain the - // escrow address by allowing more tokens to be sent back then were escrowed. - return errorsmod.Wrap(err, "unable to unescrow tokens, this may be caused by a malicious counterparty module or a bug: please open an issue on counterparty module") + if err := k.unescrowToken(ctx, escrowAddress, receiver, token); err != nil { + return err } - // track the total amount in escrow keyed by denomination to allow for efficient iteration - currentTotalEscrow := k.GetTotalEscrowForDenom(ctx, token.GetDenom()) - newTotalEscrow := currentTotalEscrow.Sub(token.Amount) - k.SetTotalEscrowForDenom(ctx, token.GetDenom(), newTotalEscrow) - defer func() { if transferAmount.IsInt64() { telemetry.SetGaugeWithLabels( @@ -367,20 +349,7 @@ func (k Keeper) refundPacketToken(ctx sdk.Context, packet channeltypes.Packet, d if types.SenderChainIsSource(packet.GetSourcePort(), packet.GetSourceChannel(), data.Denom) { // unescrow tokens back to sender escrowAddress := types.GetEscrowAddress(packet.GetSourcePort(), packet.GetSourceChannel()) - if err := k.bankKeeper.SendCoins(ctx, escrowAddress, sender, sdk.NewCoins(token)); err != nil { - // NOTE: this error is only expected to occur given an unexpected bug or a malicious - // counterparty module. The bug may occur in bank or any part of the code that allows - // the escrow address to be drained. A malicious counterparty module could drain the - // escrow address by allowing more tokens to be sent back then were escrowed. - return errorsmod.Wrap(err, "unable to unescrow tokens, this may be caused by a malicious counterparty module or a bug: please open an issue on counterparty module") - } - - // track the total amount in escrow keyed by denomination to allow for efficient iteration - currentTotalEscrow := k.GetTotalEscrowForDenom(ctx, token.GetDenom()) - newTotalEscrow := currentTotalEscrow.Sub(token.Amount) - k.SetTotalEscrowForDenom(ctx, token.GetDenom(), newTotalEscrow) - - return nil + return k.unescrowToken(ctx, escrowAddress, sender, token) } // mint vouchers back to sender @@ -397,6 +366,41 @@ func (k Keeper) refundPacketToken(ctx sdk.Context, packet channeltypes.Packet, d return nil } +// escrowToken will send the given token from the provided sender to the escrow address. It will also +// update the total escrowed amount by adding the escrowed token to the current total escrow. +func (k Keeper) escrowToken(ctx sdk.Context, sender, escrowAddress sdk.AccAddress, token sdk.Coin) error { + if err := k.bankKeeper.SendCoins(ctx, sender, escrowAddress, sdk.NewCoins(token)); err != nil { + // failure is expected for insufficient balances + return err + } + + // track the total amount in escrow keyed by denomination to allow for efficient iteration + currentTotalEscrow := k.GetTotalEscrowForDenom(ctx, token.GetDenom()) + newTotalEscrow := currentTotalEscrow.Add(token.Amount) + k.SetTotalEscrowForDenom(ctx, token.GetDenom(), newTotalEscrow) + + return nil +} + +// unescrowToken will send the given token from the escrow address to the provided receiver. It will also +// update the total escrow by deducting the unescrowed token from the current total escrow. +func (k Keeper) unescrowToken(ctx sdk.Context, escrowAddress, receiver sdk.AccAddress, token sdk.Coin) error { + if err := k.bankKeeper.SendCoins(ctx, escrowAddress, receiver, sdk.NewCoins(token)); err != nil { + // NOTE: this error is only expected to occur given an unexpected bug or a malicious + // counterparty module. The bug may occur in bank or any part of the code that allows + // the escrow address to be drained. A malicious counterparty module could drain the + // escrow address by allowing more tokens to be sent back then were escrowed. + return errorsmod.Wrap(err, "unable to unescrow tokens, this may be caused by a malicious counterparty module or a bug: please open an issue on counterparty module") + } + + // track the total amount in escrow keyed by denomination to allow for efficient iteration + currentTotalEscrow := k.GetTotalEscrowForDenom(ctx, token.GetDenom()) + newTotalEscrow := currentTotalEscrow.Sub(token.Amount) + k.SetTotalEscrowForDenom(ctx, token.GetDenom(), newTotalEscrow) + + return nil +} + // DenomPathFromHash returns the full denomination path prefix from an ibc denom with a hash // component. func (k Keeper) DenomPathFromHash(ctx sdk.Context, denom string) (string, error) {