Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: mint and transfer funds back to escrow account on timeout or ack error #173

Merged
merged 3 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion .github/workflows/golangci-lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ jobs:
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
with:
version: v1.52
version: v1.55.2
working-directory: ${{ matrix.workdir }}
args: --timeout=5m
43 changes: 26 additions & 17 deletions middleware/packet-forward-middleware/packetforward/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,48 +141,57 @@ func (k *Keeper) WriteAcknowledgementForForwardedPacket(
}
}

amount, ok := sdk.NewIntFromString(data.Amount)
if !ok {
return fmt.Errorf("failed to parse amount from packet data for forward refund: %s", data.Amount)
}

denomTrace := transfertypes.ParseDenomTrace(fullDenomPath)
token := sdk.NewCoin(denomTrace.IBCDenom(), amount)

escrowAddress := transfertypes.GetEscrowAddress(packet.SourcePort, packet.SourceChannel)
refundEscrowAddress := transfertypes.GetEscrowAddress(inFlightPacket.RefundPortId, inFlightPacket.RefundChannelId)

newToken := sdk.NewCoins(token)

if transfertypes.SenderChainIsSource(packet.SourcePort, packet.SourceChannel, fullDenomPath) {
// funds were moved to escrow account for transfer, so they need to either:
// - move to the other escrow account, in the case of native denom
// - burn

amount, ok := sdk.NewIntFromString(data.Amount)
if !ok {
return fmt.Errorf("failed to parse amount from packet data for forward refund: %s", data.Amount)
}
denomTrace := transfertypes.ParseDenomTrace(fullDenomPath)
token := sdk.NewCoin(denomTrace.IBCDenom(), amount)

escrowAddress := transfertypes.GetEscrowAddress(packet.SourcePort, packet.SourceChannel)

if transfertypes.SenderChainIsSource(inFlightPacket.RefundPortId, inFlightPacket.RefundChannelId, fullDenomPath) {
// transfer funds from escrow account for forwarded packet to escrow account going back for refund.

refundEscrowAddress := transfertypes.GetEscrowAddress(inFlightPacket.RefundPortId, inFlightPacket.RefundChannelId)

if err := k.bankKeeper.SendCoins(
ctx, escrowAddress, refundEscrowAddress, sdk.NewCoins(token),
ctx, escrowAddress, refundEscrowAddress, newToken,
); err != nil {
return fmt.Errorf("failed to send coins from escrow account to refund escrow account: %w", err)
}
} else {
// transfer the coins from the escrow account to the module account and burn them.

if err := k.bankKeeper.SendCoinsFromAccountToModule(
ctx, escrowAddress, transfertypes.ModuleName, sdk.NewCoins(token),
ctx, escrowAddress, transfertypes.ModuleName, newToken,
); err != nil {
return fmt.Errorf("failed to send coins from escrow to module account for burn: %w", err)
}

if err := k.bankKeeper.BurnCoins(
ctx, transfertypes.ModuleName, sdk.NewCoins(token),
ctx, transfertypes.ModuleName, newToken,
); err != nil {
// NOTE: should not happen as the module account was
// retrieved on the step above and it has enough balace
// to burn.
panic(fmt.Sprintf("cannot burn coins after a successful send from escrow account to module account: %v", err))
}
}
} else {
// Funds in the escrow account were burned,
// so on a timeout or acknowledgement error we need to mint the funds back to the escrow account.
if err := k.bankKeeper.MintCoins(ctx, transfertypes.ModuleName, newToken); err != nil {
return fmt.Errorf("cannot mint coins to the %s module account: %v", transfertypes.ModuleName, err)
}

if err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, transfertypes.ModuleName, refundEscrowAddress, newToken); err != nil {
return fmt.Errorf("cannot send coins from the %s module to the escrow account %s: %v", transfertypes.ModuleName, refundEscrowAddress, err)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ type DistributionKeeper interface {
// BankKeeper defines the expected bank keeper
type BankKeeper interface {
SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error
SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error
BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error
}
28 changes: 28 additions & 0 deletions middleware/packet-forward-middleware/test/mock/bank_keeper.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading