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  }