github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/app/refund/refund.go (about)

     1  package refund
     2  
     3  import (
     4  	"math/big"
     5  	"sync"
     6  
     7  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/ante"
     8  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/keeper"
     9  
    10  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    11  	sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors"
    12  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/innertx"
    13  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth"
    14  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/exported"
    15  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/types"
    16  )
    17  
    18  func NewGasRefundHandler(ak auth.AccountKeeper, sk types.SupplyKeeper, ik innertx.InnerTxKeeper) sdk.GasRefundHandler {
    19  	evmGasRefundHandler := NewGasRefundDecorator(ak, sk, ik)
    20  
    21  	return func(
    22  		ctx sdk.Context, tx sdk.Tx,
    23  	) (refundFee sdk.Coins, err error) {
    24  		var gasRefundHandler sdk.GasRefundHandler
    25  
    26  		if tx.GetType() == sdk.EvmTxType {
    27  			gasRefundHandler = evmGasRefundHandler
    28  		} else {
    29  			return nil, nil
    30  		}
    31  		return gasRefundHandler(ctx, tx)
    32  	}
    33  }
    34  
    35  type Handler struct {
    36  	ak           keeper.AccountKeeper
    37  	supplyKeeper types.SupplyKeeper
    38  	ik           innertx.InnerTxKeeper
    39  }
    40  
    41  func (handler Handler) GasRefund(ctx sdk.Context, tx sdk.Tx) (sdk.Coins, error) {
    42  	return gasRefund(handler.ik, handler.ak, handler.supplyKeeper, ctx, tx)
    43  }
    44  
    45  type accountKeeperInterface interface {
    46  	SetAccount(ctx sdk.Context, acc exported.Account)
    47  	GetAccount(ctx sdk.Context, addr sdk.AccAddress) exported.Account
    48  }
    49  
    50  func gasRefund(ik innertx.InnerTxKeeper, ak accountKeeperInterface, sk types.SupplyKeeper, ctx sdk.Context, tx sdk.Tx) (refundGasFee sdk.Coins, err error) {
    51  	currentGasMeter := ctx.GasMeter()
    52  	ctx.SetGasMeter(sdk.NewInfiniteGasMeter())
    53  
    54  	gasLimit := currentGasMeter.Limit()
    55  	gasUsed := currentGasMeter.GasConsumed()
    56  
    57  	if gasUsed >= gasLimit {
    58  		return nil, nil
    59  	}
    60  
    61  	feeTx, ok := tx.(ante.FeeTx)
    62  	if !ok {
    63  		return nil, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx")
    64  	}
    65  
    66  	feePayer := feeTx.FeePayer(ctx)
    67  	feePayerAcc := ak.GetAccount(ctx, feePayer)
    68  	if feePayerAcc == nil {
    69  		return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "fee payer address: %s does not exist", feePayer)
    70  	}
    71  
    72  	gas := feeTx.GetGas()
    73  	fees := feeTx.GetFee()
    74  	gasFees := calculateRefundFees(gasUsed, gas, fees)
    75  	newCoins := feePayerAcc.GetCoins().Add(gasFees...)
    76  
    77  	// set coins and record innertx
    78  	err = feePayerAcc.SetCoins(newCoins)
    79  	if !ctx.IsCheckTx() {
    80  		fromAddr := sk.GetModuleAddress(types.FeeCollectorName)
    81  		ik.UpdateInnerTx(ctx.TxBytes(), ctx.BlockHeight(), innertx.CosmosDepth, fromAddr, feePayerAcc.GetAddress(), innertx.CosmosCallType, innertx.SendCallName, gasFees, err)
    82  	}
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  	ak.SetAccount(ctx, feePayerAcc)
    87  
    88  	return gasFees, nil
    89  }
    90  
    91  func NewGasRefundDecorator(ak auth.AccountKeeper, sk types.SupplyKeeper, ik innertx.InnerTxKeeper) sdk.GasRefundHandler {
    92  	chandler := Handler{
    93  		ak:           ak,
    94  		supplyKeeper: sk,
    95  		ik:           ik,
    96  	}
    97  	return chandler.GasRefund
    98  }
    99  
   100  var bigIntsPool = &sync.Pool{
   101  	New: func() interface{} {
   102  		return &[2]big.Int{}
   103  	},
   104  }
   105  
   106  func calculateRefundFees(gasUsed uint64, gas uint64, fees sdk.DecCoins) sdk.Coins {
   107  	bitInts := bigIntsPool.Get().(*[2]big.Int)
   108  	defer bigIntsPool.Put(bitInts)
   109  
   110  	refundFees := make(sdk.Coins, len(fees))
   111  	for i, fee := range fees {
   112  		gasPrice := bitInts[0].SetUint64(gas)
   113  		gasPrice = gasPrice.Div(fee.Amount.Int, gasPrice)
   114  
   115  		gasConsumed := bitInts[1].SetUint64(gasUsed)
   116  		gasConsumed = gasConsumed.Mul(gasPrice, gasConsumed)
   117  
   118  		gasCost := sdk.NewDecCoinFromDec(fee.Denom, sdk.NewDecWithBigIntAndPrec(gasConsumed, sdk.Precision))
   119  		gasRefund := fee.Sub(gasCost)
   120  
   121  		refundFees[i] = gasRefund
   122  	}
   123  	return refundFees
   124  }
   125  
   126  // CalculateRefundFees provides the way to calculate the refunded gas with gasUsed, fees and gasPrice,
   127  // as refunded gas = fees - gasPrice * gasUsed
   128  func CalculateRefundFees(gasUsed uint64, fees sdk.DecCoins, gasPrice *big.Int) sdk.Coins {
   129  	gas := new(big.Int).Div(fees[0].Amount.BigInt(), gasPrice).Uint64()
   130  	return calculateRefundFees(gasUsed, gas, fees)
   131  }