Skip to content

Latest commit

 

History

History
84 lines (54 loc) · 2.21 KB

020.md

File metadata and controls

84 lines (54 loc) · 2.21 KB

Petite Fleece Cheetah

High

Refund mechanism doesn't make sure that there is a fee granter

Summary

There is currently a refund logic that transfers the funds to a fee payer.

Root Cause

The root cause lies in the fact that the transaction fees can be paid by other enitities and the funds are returned to a fee payer and not a granter.

Internal Pre-conditions

External Pre-conditions

Fee granter has to pay for the fees.

Attack Path

Fee grantor paid for the fees but the funds are returned to a fee payer.

Impact

Loss of funds for a fee granter.

PoC

Consider the current refund mechanism:

https://github.com/sherlock-audit/2024-12-babylon/blob/main/babylon/x/incentive/keeper/refundable_msg_index.go#L10-23

// RefundTx refunds the given tx by sending the fee back to the fee payer.
func (k Keeper) RefundTx(ctx context.Context, tx sdk.FeeTx) error {
	txFee := tx.GetFee()
	if txFee.IsZero() {
		// not possible with the global min gas price mechanism
		// but having this check for compatibility in the future
		return nil
	}
	txFeePayer := tx.FeePayer()

	return k.bankKeeper.SendCoinsFromModuleToAccount(ctx, k.feeCollectorName, txFeePayer, txFee)
}

The problem is that the fees are refunded to the feePayer (the sender of the message) without taking into account the fact that the actual payer can be feeGranter resulting in a loss of funds for the granter:

type FeeTx interface {
	[Tx](https://pkg.go.dev/github.com/cosmos/cosmos-sdk/types#Tx)
	GetGas() [uint64](https://pkg.go.dev/builtin#uint64)
	GetFee() [Coins](https://pkg.go.dev/github.com/cosmos/cosmos-sdk/types#Coins)
	FeePayer() [][byte](https://pkg.go.dev/builtin#byte)
	FeeGranter() [][byte](https://pkg.go.dev/builtin#byte)
}

Mitigation

Check out the Celestia solution for this issue:

https://github.com/rootulp/celestia-app/blob/d10fdbd4e5507f8449747a2388c4657f593b691b/app/posthandler/refund_gas_remaining.go#L133-L138

// getRecipient returns the address that should receive the refund.
func getRecipient(feeTx sdk.FeeTx) sdk.AccAddress {
	if feeGranter := feeTx.FeeGranter(); feeGranter != nil {
		return feeGranter
	}
	return feeTx.FeePayer()
}

Here the funds are actually sent to the right entity.