-
Notifications
You must be signed in to change notification settings - Fork 206
/
ante.go
181 lines (151 loc) · 5.08 KB
/
ante.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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
package ante
import (
"encoding/json"
wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
devgastypes "github.com/NibiruChain/nibiru/x/devgas/v1/types"
)
var _ sdk.AnteDecorator = (*DevGasPayoutDecorator)(nil)
// DevGasPayoutDecorator Run his after we already deduct the fee from the
// account with the ante.NewDeductFeeDecorator() decorator. We pull funds from
// the FeeCollector ModuleAccount
type DevGasPayoutDecorator struct {
bankKeeper BankKeeper
devgasKeeper IDevGasKeeper
}
func NewDevGasPayoutDecorator(
bk BankKeeper, fs IDevGasKeeper,
) DevGasPayoutDecorator {
return DevGasPayoutDecorator{
bankKeeper: bk,
devgasKeeper: fs,
}
}
func (a DevGasPayoutDecorator) AnteHandle(
ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler,
) (newCtx sdk.Context, err error) {
feeTx, ok := tx.(sdk.FeeTx)
if !ok {
return ctx, sdkerrors.ErrTxDecode.Wrap("Tx must be a FeeTx")
}
err = a.devGasPayout(
ctx, feeTx,
)
if err != nil {
return ctx, sdkerrors.ErrInsufficientFunds.Wrap(err.Error())
}
return next(ctx, tx, simulate)
}
// devGasPayout takes the total fees and redistributes 50% (or param set) to
// the contract developers provided they opted-in to payments.
func (a DevGasPayoutDecorator) devGasPayout(
ctx sdk.Context,
tx sdk.FeeTx,
) error {
params := a.devgasKeeper.GetParams(ctx)
if !params.EnableFeeShare {
return nil
}
toPay, err := a.getWithdrawAddressesFromMsgs(ctx, tx.GetMsgs())
if err != nil {
return err
}
// Do nothing if no one needs payment
if len(toPay) == 0 {
return nil
}
feesPaidOutput, err := a.settleFeePayments(ctx, toPay, params, tx.GetFee())
if err != nil {
return err
}
bz, err := json.Marshal(feesPaidOutput)
if err != nil {
return devgastypes.ErrFeeSharePayment.Wrapf("failed to marshal feesPaidOutput: %s", err.Error())
}
return ctx.EventManager().EmitTypedEvent(
&devgastypes.EventPayoutDevGas{Payouts: string(bz)},
)
}
type FeeSharePayoutEventOutput struct {
WithdrawAddress sdk.AccAddress `json:"withdraw_address"`
FeesPaid sdk.Coins `json:"fees_paid"`
}
// settleFeePayments sends the funds to the contract developers
func (a DevGasPayoutDecorator) settleFeePayments(
ctx sdk.Context, toPay []sdk.AccAddress, params devgastypes.ModuleParams, totalFees sdk.Coins,
) ([]FeeSharePayoutEventOutput, error) {
allowedFees := getAllowedFees(params, totalFees)
numPairs := len(toPay)
feesPaidOutput := make([]FeeSharePayoutEventOutput, numPairs)
if numPairs > 0 {
govPercent := params.DeveloperShares
splitFees := FeePayLogic(allowedFees, govPercent, numPairs)
// pay fees evenly between all withdraw addresses
for i, withdrawAddr := range toPay {
err := a.bankKeeper.SendCoinsFromModuleToAccount(ctx, authtypes.FeeCollectorName, withdrawAddr, splitFees)
feesPaidOutput[i] = FeeSharePayoutEventOutput{
WithdrawAddress: withdrawAddr,
FeesPaid: splitFees,
}
if err != nil {
return nil, devgastypes.ErrFeeSharePayment.Wrapf("failed to pay allowedFees to contract developer: %s", err.Error())
}
}
}
return feesPaidOutput, nil
}
// getAllowedFees gets the allowed fees to be paid based on the module
// parameters of x/devgas
func getAllowedFees(params devgastypes.ModuleParams, totalFees sdk.Coins) sdk.Coins {
// Get only allowed governance fees to be paid (helps for taxes)
var allowedFees sdk.Coins
if len(params.AllowedDenoms) == 0 {
// If empty, we allow all denoms to be used as payment
allowedFees = totalFees
} else {
for _, fee := range totalFees.Sort() {
for _, allowed := range params.AllowedDenoms {
if fee.Denom == allowed {
allowedFees = allowedFees.Add(fee)
}
}
}
}
return allowedFees
}
// getWithdrawAddressesFromMsgs returns a list of all contract addresses that
// have opted-in to receiving payments
func (a DevGasPayoutDecorator) getWithdrawAddressesFromMsgs(ctx sdk.Context, msgs []sdk.Msg) ([]sdk.AccAddress, error) {
toPay := make([]sdk.AccAddress, 0)
for _, msg := range msgs {
if _, ok := msg.(*wasmtypes.MsgExecuteContract); ok {
contractAddr, err := sdk.AccAddressFromBech32(
msg.(*wasmtypes.MsgExecuteContract).Contract,
)
if err != nil {
return nil, err
}
shareData, _ := a.devgasKeeper.GetFeeShare(ctx, contractAddr)
withdrawAddr := shareData.GetWithdrawerAddr()
if withdrawAddr != nil && !withdrawAddr.Empty() {
toPay = append(toPay, withdrawAddr)
}
}
}
return toPay, nil
}
// FeePayLogic takes the total fees and splits them based on the governance
// params and the number of contracts we are executing on. This returns the
// amount of fees each contract developer should get. tested in ante_test.go
func FeePayLogic(fees sdk.Coins, govPercent sdk.Dec, numPairs int) sdk.Coins {
var splitFees sdk.Coins
for _, c := range fees.Sort() {
rewardAmount := govPercent.MulInt(c.Amount).QuoInt64(int64(numPairs)).RoundInt()
if !rewardAmount.IsZero() {
splitFees = splitFees.Add(sdk.NewCoin(c.Denom, rewardAmount))
}
}
return splitFees
}