-
Notifications
You must be signed in to change notification settings - Fork 169
/
ante.go
177 lines (144 loc) · 5.33 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
package ante
import (
"encoding/json"
wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types"
errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/authz"
feeshare "github.com/CosmosContracts/juno/v22/x/feeshare/types"
)
// FeeSharePayoutDecorator Run his after we already deduct the fee from the account with
// the ante.NewDeductFeeDecorator() decorator. We pull funds from the FeeCollector ModuleAccount
type FeeSharePayoutDecorator struct {
bankKeeper BankKeeper
feesharekeeper FeeShareKeeper
}
func NewFeeSharePayoutDecorator(bk BankKeeper, fs FeeShareKeeper) FeeSharePayoutDecorator {
return FeeSharePayoutDecorator{
bankKeeper: bk,
feesharekeeper: fs,
}
}
func (fsd FeeSharePayoutDecorator) 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, errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx")
}
err = fsd.FeeSharePayout(ctx, fsd.bankKeeper, feeTx.GetFee(), fsd.feesharekeeper, tx.GetMsgs())
if err != nil {
return ctx, errorsmod.Wrapf(sdkerrors.ErrInsufficientFunds, err.Error())
}
return next(ctx, tx, simulate)
}
// 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
}
type FeeSharePayoutEventOutput struct {
WithdrawAddress sdk.AccAddress `json:"withdraw_address"`
FeesPaid sdk.Coins `json:"fees_paid"`
}
// Loop through all messages and add the withdraw address to the list of addresses to pay
// if the contract opted-in to fee sharing
func addNewFeeSharePayoutsForMsgs(ctx sdk.Context, fsk FeeShareKeeper, toPay *[]sdk.AccAddress, msgs []sdk.Msg) error {
for _, msg := range msgs {
// Check if an authz message, loop through all inner messages, and recursively call this function
if authzMsg, ok := msg.(*authz.MsgExec); ok {
innerMsgs, err := authzMsg.GetMessages()
if err != nil {
return errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "cannot unmarshal authz exec msgs")
}
// Recursively call this function with the inner messages
err = addNewFeeSharePayoutsForMsgs(ctx, fsk, toPay, innerMsgs)
if err != nil {
return err
}
}
// If an execute contract message, check if the contract opted-in to fee sharing,
// and if so, add the withdraw address to the list of addresses to pay
if execContractMsg, ok := msg.(*wasmtypes.MsgExecuteContract); ok {
contractAddr, err := sdk.AccAddressFromBech32(execContractMsg.Contract)
if err != nil {
return err
}
shareData, _ := fsk.GetFeeShare(ctx, contractAddr)
withdrawAddr := shareData.GetWithdrawerAddr()
if withdrawAddr != nil && !withdrawAddr.Empty() {
*toPay = append(*toPay, withdrawAddr)
}
}
}
return nil
}
// FeeSharePayout takes the total fees and redistributes 50% (or param set) to the contract developers
// provided they opted-in to payments.
func (fsd FeeSharePayoutDecorator) FeeSharePayout(ctx sdk.Context, bankKeeper BankKeeper, totalFees sdk.Coins, fsk FeeShareKeeper, msgs []sdk.Msg) error {
params := fsk.GetParams(ctx)
if !params.EnableFeeShare {
return nil
}
// Get valid withdraw addresses from contracts
toPay := make([]sdk.AccAddress, 0)
// Add fee share payouts for each msg
err := addNewFeeSharePayoutsForMsgs(ctx, fsk, &toPay, msgs)
if err != nil {
return err
}
// Do nothing if no one needs payment
if len(toPay) == 0 {
return nil
}
// Get only allowed governance fees to be paid (helps for taxes)
var fees sdk.Coins
if len(params.AllowedDenoms) == 0 {
// If empty, we allow all denoms to be used as payment
fees = totalFees
} else {
for _, fee := range totalFees.Sort() {
for _, allowed := range params.AllowedDenoms {
if fee.Denom == allowed {
fees = fees.Add(fee)
}
}
}
}
numPairs := len(toPay)
feesPaidOutput := make([]FeeSharePayoutEventOutput, numPairs)
if numPairs > 0 {
govPercent := params.DeveloperShares
splitFees := FeePayLogic(fees, govPercent, numPairs)
// pay fees evenly between all withdraw addresses
for i, withdrawAddr := range toPay {
err := bankKeeper.SendCoinsFromModuleToAccount(ctx, authtypes.FeeCollectorName, withdrawAddr, splitFees)
feesPaidOutput[i] = FeeSharePayoutEventOutput{
WithdrawAddress: withdrawAddr,
FeesPaid: splitFees,
}
if err != nil {
return errorsmod.Wrapf(feeshare.ErrFeeSharePayment, "failed to pay fees to contract developer: %s", err.Error())
}
}
}
bz, err := json.Marshal(feesPaidOutput)
if err != nil {
return errorsmod.Wrapf(feeshare.ErrFeeSharePayment, "failed to marshal feesPaidOutput: %s", err.Error())
}
ctx.EventManager().EmitEvent(
sdk.NewEvent(
feeshare.EventTypePayoutFeeShare,
sdk.NewAttribute(feeshare.AttributeWithdrawPayouts, string(bz))),
)
return nil
}