github.com/Finschia/finschia-sdk@v0.49.1/x/feegrant/keeper/keeper.go (about) 1 package keeper 2 3 import ( 4 "fmt" 5 6 "github.com/Finschia/ostracon/libs/log" 7 8 "github.com/Finschia/finschia-sdk/codec" 9 sdk "github.com/Finschia/finschia-sdk/types" 10 sdkerrors "github.com/Finschia/finschia-sdk/types/errors" 11 "github.com/Finschia/finschia-sdk/x/auth/ante" 12 "github.com/Finschia/finschia-sdk/x/feegrant" 13 ) 14 15 // Keeper manages state of all fee grants, as well as calculating approval. 16 // It must have a codec with all available allowances registered. 17 type Keeper struct { 18 cdc codec.BinaryCodec 19 storeKey sdk.StoreKey 20 authKeeper feegrant.AccountKeeper 21 } 22 23 var _ ante.FeegrantKeeper = &Keeper{} 24 25 // NewKeeper creates a fee grant Keeper 26 func NewKeeper(cdc codec.BinaryCodec, storeKey sdk.StoreKey, ak feegrant.AccountKeeper) Keeper { 27 return Keeper{ 28 cdc: cdc, 29 storeKey: storeKey, 30 authKeeper: ak, 31 } 32 } 33 34 // Logger returns a module-specific logger. 35 func (k Keeper) Logger(ctx sdk.Context) log.Logger { 36 return ctx.Logger().With("module", fmt.Sprintf("x/%s", feegrant.ModuleName)) 37 } 38 39 // GrantAllowance creates a new grant 40 func (k Keeper) GrantAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress, feeAllowance feegrant.FeeAllowanceI) error { 41 // create the account if it is not in account state 42 granteeAcc := k.authKeeper.GetAccount(ctx, grantee) 43 if granteeAcc == nil { 44 granteeAcc = k.authKeeper.NewAccountWithAddress(ctx, grantee) 45 k.authKeeper.SetAccount(ctx, granteeAcc) 46 } 47 48 store := ctx.KVStore(k.storeKey) 49 key := feegrant.FeeAllowanceKey(granter, grantee) 50 grant, err := feegrant.NewGrant(granter, grantee, feeAllowance) 51 if err != nil { 52 return err 53 } 54 55 bz, err := k.cdc.Marshal(&grant) 56 if err != nil { 57 return err 58 } 59 60 store.Set(key, bz) 61 62 ctx.EventManager().EmitEvent( 63 sdk.NewEvent( 64 feegrant.EventTypeSetFeeGrant, 65 sdk.NewAttribute(feegrant.AttributeKeyGranter, grant.Granter), 66 sdk.NewAttribute(feegrant.AttributeKeyGrantee, grant.Grantee), 67 ), 68 ) 69 70 return nil 71 } 72 73 // revokeAllowance removes an existing grant 74 func (k Keeper) revokeAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress) error { 75 _, err := k.getGrant(ctx, granter, grantee) 76 if err != nil { 77 return err 78 } 79 80 store := ctx.KVStore(k.storeKey) 81 key := feegrant.FeeAllowanceKey(granter, grantee) 82 store.Delete(key) 83 84 ctx.EventManager().EmitEvent( 85 sdk.NewEvent( 86 feegrant.EventTypeRevokeFeeGrant, 87 sdk.NewAttribute(feegrant.AttributeKeyGranter, granter.String()), 88 sdk.NewAttribute(feegrant.AttributeKeyGrantee, grantee.String()), 89 ), 90 ) 91 return nil 92 } 93 94 // GetAllowance returns the allowance between the granter and grantee. 95 // If there is none, it returns nil, nil. 96 // Returns an error on parsing issues 97 func (k Keeper) GetAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress) (feegrant.FeeAllowanceI, error) { 98 grant, err := k.getGrant(ctx, granter, grantee) 99 if err != nil { 100 return nil, err 101 } 102 103 return grant.GetGrant() 104 } 105 106 // getGrant returns entire grant between both accounts 107 func (k Keeper) getGrant(ctx sdk.Context, granter, grantee sdk.AccAddress) (*feegrant.Grant, error) { 108 store := ctx.KVStore(k.storeKey) 109 key := feegrant.FeeAllowanceKey(granter, grantee) 110 bz := store.Get(key) 111 if len(bz) == 0 { 112 return nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "fee-grant not found") 113 } 114 115 var feegrant feegrant.Grant 116 if err := k.cdc.Unmarshal(bz, &feegrant); err != nil { 117 return nil, err 118 } 119 120 return &feegrant, nil 121 } 122 123 // IterateAllFeeAllowances iterates over all the grants in the store. 124 // Callback to get all data, returns true to stop, false to keep reading 125 // Calling this without pagination is very expensive and only designed for export genesis 126 func (k Keeper) IterateAllFeeAllowances(ctx sdk.Context, cb func(grant feegrant.Grant) bool) error { 127 store := ctx.KVStore(k.storeKey) 128 iter := sdk.KVStorePrefixIterator(store, feegrant.FeeAllowanceKeyPrefix) 129 defer iter.Close() 130 131 stop := false 132 for ; iter.Valid() && !stop; iter.Next() { 133 bz := iter.Value() 134 var feeGrant feegrant.Grant 135 if err := k.cdc.Unmarshal(bz, &feeGrant); err != nil { 136 return err 137 } 138 139 stop = cb(feeGrant) 140 } 141 142 return nil 143 } 144 145 // UseGrantedFees will try to pay the given fee from the granter's account as requested by the grantee 146 func (k Keeper) UseGrantedFees(ctx sdk.Context, granter, grantee sdk.AccAddress, fee sdk.Coins, msgs []sdk.Msg) error { 147 f, err := k.getGrant(ctx, granter, grantee) 148 if err != nil { 149 return err 150 } 151 152 grant, err := f.GetGrant() 153 if err != nil { 154 return err 155 } 156 157 remove, err := grant.Accept(ctx, fee, msgs) 158 159 if remove { 160 // Ignoring the `revokeFeeAllowance` error, because the user has enough grants to perform this transaction. 161 _ = k.revokeAllowance(ctx, granter, grantee) 162 if err != nil { 163 return err 164 } 165 166 emitUseGrantEvent(ctx, granter.String(), grantee.String()) 167 168 return nil 169 } 170 171 if err != nil { 172 return err 173 } 174 175 emitUseGrantEvent(ctx, granter.String(), grantee.String()) 176 177 // if fee allowance is accepted, store the updated state of the allowance 178 return k.GrantAllowance(ctx, granter, grantee, grant) 179 } 180 181 func emitUseGrantEvent(ctx sdk.Context, granter, grantee string) { 182 ctx.EventManager().EmitEvent( 183 sdk.NewEvent( 184 feegrant.EventTypeUseFeeGrant, 185 sdk.NewAttribute(feegrant.AttributeKeyGranter, granter), 186 sdk.NewAttribute(feegrant.AttributeKeyGrantee, grantee), 187 ), 188 ) 189 } 190 191 // InitGenesis will initialize the keeper from a *previously validated* GenesisState 192 func (k Keeper) InitGenesis(ctx sdk.Context, data *feegrant.GenesisState) error { 193 for _, f := range data.Allowances { 194 granter, err := sdk.AccAddressFromBech32(f.Granter) 195 if err != nil { 196 return err 197 } 198 grantee, err := sdk.AccAddressFromBech32(f.Grantee) 199 if err != nil { 200 return err 201 } 202 203 grant, err := f.GetGrant() 204 if err != nil { 205 return err 206 } 207 208 err = k.GrantAllowance(ctx, granter, grantee, grant) 209 if err != nil { 210 return err 211 } 212 } 213 return nil 214 } 215 216 // ExportGenesis will dump the contents of the keeper into a serializable GenesisState. 217 func (k Keeper) ExportGenesis(ctx sdk.Context) (*feegrant.GenesisState, error) { 218 var grants []feegrant.Grant 219 220 err := k.IterateAllFeeAllowances(ctx, func(grant feegrant.Grant) bool { 221 grants = append(grants, grant) 222 return false 223 }) 224 225 return &feegrant.GenesisState{ 226 Allowances: grants, 227 }, err 228 }