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 }