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