github.com/Finschia/finschia-sdk@v0.49.1/x/fswap/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  	"github.com/Finschia/finschia-sdk/store/prefix"
    10  	storetypes "github.com/Finschia/finschia-sdk/store/types"
    11  	sdk "github.com/Finschia/finschia-sdk/types"
    12  	sdkerrors "github.com/Finschia/finschia-sdk/types/errors"
    13  	bank "github.com/Finschia/finschia-sdk/x/bank/types"
    14  	"github.com/Finschia/finschia-sdk/x/fswap/types"
    15  )
    16  
    17  type Keeper struct {
    18  	cdc       codec.BinaryCodec
    19  	storeKey  storetypes.StoreKey
    20  	config    types.Config
    21  	authority string
    22  	BankKeeper
    23  }
    24  
    25  func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey, config types.Config, authority string, ak AccountKeeper, bk BankKeeper) Keeper {
    26  	if _, err := sdk.AccAddressFromBech32(authority); err != nil {
    27  		panic("authority is not a valid acc address")
    28  	}
    29  
    30  	if addr := ak.GetModuleAddress(types.ModuleName); addr == nil {
    31  		panic("fswap module account has not been set")
    32  	}
    33  
    34  	found := false
    35  	for _, addr := range types.AuthorityCandidates() {
    36  		if authority == addr.String() {
    37  			found = true
    38  			break
    39  		}
    40  	}
    41  
    42  	if !found {
    43  		panic("x/fswap authority must be ether gov or foundation module account")
    44  	}
    45  
    46  	return Keeper{
    47  		cdc,
    48  		storeKey,
    49  		config,
    50  		authority,
    51  		bk,
    52  	}
    53  }
    54  
    55  func (k Keeper) Logger(ctx sdk.Context) log.Logger {
    56  	return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
    57  }
    58  
    59  func (k Keeper) Swap(ctx sdk.Context, addr sdk.AccAddress, fromCoinAmount sdk.Coin, toDenom string) error {
    60  	swap, err := k.getSwap(ctx, fromCoinAmount.Denom, toDenom)
    61  	if err != nil {
    62  		return err
    63  	}
    64  
    65  	newCoinAmountInt := CalcSwap(swap.SwapRate, fromCoinAmount.Amount)
    66  	newCoinAmount := sdk.NewCoin(toDenom, newCoinAmountInt)
    67  	swapped, err := k.getSwapped(ctx, swap.GetFromDenom(), swap.GetToDenom())
    68  	if err != nil {
    69  		return err
    70  	}
    71  
    72  	updateSwapped, err := k.updateSwapped(ctx, swapped, fromCoinAmount, newCoinAmount)
    73  	if err != nil {
    74  		return err
    75  	}
    76  
    77  	if err := k.checkSwapCap(swap, updateSwapped); err != nil {
    78  		return err
    79  	}
    80  
    81  	if err := k.SendCoinsFromAccountToModule(ctx, addr, types.ModuleName, sdk.NewCoins(fromCoinAmount)); err != nil {
    82  		return err
    83  	}
    84  
    85  	if err := k.BurnCoins(ctx, types.ModuleName, sdk.NewCoins(fromCoinAmount)); err != nil {
    86  		return err
    87  	}
    88  
    89  	if err := k.MintCoins(ctx, types.ModuleName, sdk.NewCoins(newCoinAmount)); err != nil {
    90  		return err
    91  	}
    92  
    93  	if err := k.SendCoinsFromModuleToAccount(ctx, types.ModuleName, addr, sdk.NewCoins(newCoinAmount)); err != nil {
    94  		return err
    95  	}
    96  
    97  	if err := ctx.EventManager().EmitTypedEvent(&types.EventSwapCoins{
    98  		Address:        addr.String(),
    99  		FromCoinAmount: fromCoinAmount,
   100  		ToCoinAmount:   newCoinAmount,
   101  	}); err != nil {
   102  		return err
   103  	}
   104  	return nil
   105  }
   106  
   107  func (k Keeper) SetSwap(ctx sdk.Context, swap types.Swap, toDenomMetadata bank.Metadata) error {
   108  	isNewSwap := true
   109  	if _, err := k.getSwap(ctx, swap.FromDenom, swap.ToDenom); err == nil {
   110  		isNewSwap = false
   111  	}
   112  
   113  	if !isNewSwap && !k.config.UpdateAllowed {
   114  		return sdkerrors.ErrInvalidRequest.Wrap("update existing swap not allowed")
   115  	}
   116  
   117  	if isNewSwap {
   118  		if err := k.increaseSwapCount(ctx); err != nil {
   119  			return err
   120  		}
   121  	}
   122  
   123  	stats, err := k.getSwapStats(ctx)
   124  	if err != nil {
   125  		return err
   126  	}
   127  
   128  	if int(stats.SwapCount) > k.config.MaxSwaps && !k.isUnlimited() {
   129  		return types.ErrCanNotHaveMoreSwap.Wrapf("cannot make more swaps, max swaps is %d", k.config.MaxSwaps)
   130  	}
   131  
   132  	if _, ok := k.GetDenomMetaData(ctx, swap.FromDenom); !ok {
   133  		return sdkerrors.ErrInvalidRequest.Wrap("fromDenom should be existed in chain")
   134  	}
   135  
   136  	if isNewSwap {
   137  		swapped := types.Swapped{
   138  			FromCoinAmount: sdk.Coin{
   139  				Denom:  swap.GetFromDenom(),
   140  				Amount: sdk.ZeroInt(),
   141  			},
   142  			ToCoinAmount: sdk.Coin{
   143  				Denom:  swap.GetToDenom(),
   144  				Amount: sdk.ZeroInt(),
   145  			},
   146  		}
   147  		if err := k.setSwapped(ctx, swapped); err != nil {
   148  			return err
   149  		}
   150  	}
   151  
   152  	if err := k.setSwap(ctx, swap); err != nil {
   153  		return err
   154  	}
   155  
   156  	eventManager := ctx.EventManager()
   157  	if err := eventManager.EmitTypedEvent(&types.EventSetSwap{Swap: swap}); err != nil {
   158  		panic(err)
   159  	}
   160  
   161  	existingMetadata, ok := k.GetDenomMetaData(ctx, swap.ToDenom)
   162  	if !ok {
   163  		k.SetDenomMetaData(ctx, toDenomMetadata)
   164  		if err := eventManager.EmitTypedEvent(&(types.EventAddDenomMetadata{Metadata: toDenomMetadata})); err != nil {
   165  			panic(err)
   166  		}
   167  		return nil
   168  	}
   169  	if !denomMetadataEqual(existingMetadata, toDenomMetadata) {
   170  		return sdkerrors.ErrInvalidRequest.Wrap("changing existing metadata not allowed")
   171  	}
   172  
   173  	return nil
   174  }
   175  
   176  func (k Keeper) getAllSwapped(ctx sdk.Context) []types.Swapped {
   177  	swappedSlice := []types.Swapped{}
   178  	k.iterateAllSwapped(ctx, func(swapped types.Swapped) bool {
   179  		swappedSlice = append(swappedSlice, swapped)
   180  		return false
   181  	})
   182  	return swappedSlice
   183  }
   184  
   185  func (k Keeper) iterateAllSwapped(ctx sdk.Context, cb func(swapped types.Swapped) (stop bool)) {
   186  	store := ctx.KVStore(k.storeKey)
   187  	swappedDataStore := prefix.NewStore(store, swappedKeyPrefix)
   188  
   189  	iterator := swappedDataStore.Iterator(nil, nil)
   190  	defer iterator.Close()
   191  
   192  	for ; iterator.Valid(); iterator.Next() {
   193  		swapped := types.Swapped{}
   194  		k.cdc.MustUnmarshal(iterator.Value(), &swapped)
   195  		if cb(swapped) {
   196  			break
   197  		}
   198  	}
   199  }
   200  
   201  func (k Keeper) getSwapped(ctx sdk.Context, fromDenom, toDenom string) (types.Swapped, error) {
   202  	store := ctx.KVStore(k.storeKey)
   203  	key := swappedKey(fromDenom, toDenom)
   204  	bz := store.Get(key)
   205  	if bz == nil {
   206  		return types.Swapped{}, types.ErrSwappedNotFound
   207  	}
   208  
   209  	swapped := types.Swapped{}
   210  	if err := k.cdc.Unmarshal(bz, &swapped); err != nil {
   211  		return types.Swapped{}, err
   212  	}
   213  	return swapped, nil
   214  }
   215  
   216  func (k Keeper) setSwapped(ctx sdk.Context, swapped types.Swapped) error {
   217  	key := swappedKey(swapped.FromCoinAmount.Denom, swapped.ToCoinAmount.Denom)
   218  	bz, err := k.cdc.Marshal(&swapped)
   219  	if err != nil {
   220  		return err
   221  	}
   222  
   223  	store := ctx.KVStore(k.storeKey)
   224  	store.Set(key, bz)
   225  	return nil
   226  }
   227  
   228  func (k Keeper) getSwappableNewCoinAmount(ctx sdk.Context, fromDenom, toDenom string) (sdk.Coin, error) {
   229  	swap, err := k.getSwap(ctx, fromDenom, toDenom)
   230  	if err != nil {
   231  		return sdk.Coin{}, err
   232  	}
   233  
   234  	swapped, err := k.getSwapped(ctx, fromDenom, toDenom)
   235  	if err != nil {
   236  		return sdk.Coin{}, err
   237  	}
   238  
   239  	swapCap := swap.AmountCapForToDenom
   240  	remainingAmount := swapCap.Sub(swapped.GetToCoinAmount().Amount)
   241  
   242  	return sdk.NewCoin(toDenom, remainingAmount), nil
   243  }
   244  
   245  func (k Keeper) updateSwapped(ctx sdk.Context, curSwapped types.Swapped, fromAmount, toAmount sdk.Coin) (types.Swapped, error) {
   246  	updatedSwapped := types.Swapped{
   247  		FromCoinAmount: fromAmount.Add(curSwapped.FromCoinAmount),
   248  		ToCoinAmount:   toAmount.Add(curSwapped.ToCoinAmount),
   249  	}
   250  
   251  	key := swappedKey(fromAmount.Denom, toAmount.Denom)
   252  	bz, err := k.cdc.Marshal(&updatedSwapped)
   253  	if err != nil {
   254  		return types.Swapped{}, err
   255  	}
   256  
   257  	store := ctx.KVStore(k.storeKey)
   258  	store.Set(key, bz)
   259  	return updatedSwapped, nil
   260  }
   261  
   262  func (k Keeper) checkSwapCap(swap types.Swap, swapped types.Swapped) error {
   263  	swapCap := swap.AmountCapForToDenom
   264  	if swapCap.LT(swapped.ToCoinAmount.Amount) {
   265  		return types.ErrExceedSwappableToCoinAmount
   266  	}
   267  	return nil
   268  }
   269  
   270  func (k Keeper) getSwapStats(ctx sdk.Context) (types.SwapStats, error) {
   271  	store := ctx.KVStore(k.storeKey)
   272  	bz := store.Get(swapStatsKey)
   273  	if bz == nil {
   274  		return types.SwapStats{}, nil
   275  	}
   276  
   277  	stats := types.SwapStats{}
   278  	err := k.cdc.Unmarshal(bz, &stats)
   279  	if err != nil {
   280  		return types.SwapStats{}, err
   281  	}
   282  	return stats, nil
   283  }
   284  
   285  func (k Keeper) setSwapStats(ctx sdk.Context, stats types.SwapStats) error {
   286  	bz, err := k.cdc.Marshal(&stats)
   287  	if err != nil {
   288  		return err
   289  	}
   290  
   291  	store := ctx.KVStore(k.storeKey)
   292  	store.Set(swapStatsKey, bz)
   293  	return nil
   294  }
   295  
   296  func (k Keeper) validateAuthority(authority string) error {
   297  	if authority != k.authority {
   298  		return sdkerrors.ErrUnauthorized.Wrapf("invalid authority; expected %s, got %s", k.authority, authority)
   299  	}
   300  
   301  	return nil
   302  }
   303  
   304  func denomMetadataEqual(metadata, otherMetadata bank.Metadata) bool {
   305  	if metadata.Description != otherMetadata.Description {
   306  		return false
   307  	}
   308  	if len(metadata.DenomUnits) != len(otherMetadata.DenomUnits) {
   309  		return false
   310  	}
   311  	for i, unit := range metadata.DenomUnits {
   312  		if unit.Denom != otherMetadata.DenomUnits[i].Denom {
   313  			return false
   314  		}
   315  	}
   316  	if metadata.Base != otherMetadata.Base {
   317  		return false
   318  	}
   319  	if metadata.Display != otherMetadata.Display {
   320  		return false
   321  	}
   322  	if metadata.Name != otherMetadata.Name {
   323  		return false
   324  	}
   325  	if metadata.Symbol != otherMetadata.Symbol {
   326  		return false
   327  	}
   328  	return true
   329  }
   330  
   331  func (k Keeper) increaseSwapCount(ctx sdk.Context) error {
   332  	stats, err := k.getSwapStats(ctx)
   333  	if err != nil {
   334  		return err
   335  	}
   336  
   337  	prev := stats.SwapCount
   338  	stats.SwapCount++
   339  	if stats.SwapCount < prev {
   340  		return types.ErrInvalidState.Wrap("overflow detected")
   341  	}
   342  
   343  	if err := k.setSwapStats(ctx, stats); err != nil {
   344  		return err
   345  	}
   346  	return nil
   347  }
   348  
   349  func (k Keeper) setSwap(ctx sdk.Context, swap types.Swap) error {
   350  	key := swapKey(swap.FromDenom, swap.ToDenom)
   351  	bz, err := k.cdc.Marshal(&swap)
   352  	if err != nil {
   353  		return err
   354  	}
   355  
   356  	store := ctx.KVStore(k.storeKey)
   357  	store.Set(key, bz)
   358  	return nil
   359  }
   360  
   361  func (k Keeper) getSwap(ctx sdk.Context, fromDenom, toDenom string) (types.Swap, error) {
   362  	store := ctx.KVStore(k.storeKey)
   363  	key := swapKey(fromDenom, toDenom)
   364  	bz := store.Get(key)
   365  	if bz == nil {
   366  		return types.Swap{}, sdkerrors.ErrNotFound.Wrap("swap not found")
   367  	}
   368  
   369  	swap := types.Swap{}
   370  	if err := k.cdc.Unmarshal(bz, &swap); err != nil {
   371  		return types.Swap{}, err
   372  	}
   373  
   374  	return swap, nil
   375  }
   376  
   377  func (k Keeper) getAllSwaps(ctx sdk.Context) []types.Swap {
   378  	swaps := []types.Swap{}
   379  	k.iterateAllSwaps(ctx, func(swap types.Swap) bool {
   380  		swaps = append(swaps, swap)
   381  		return false
   382  	})
   383  	return swaps
   384  }
   385  
   386  func (k Keeper) iterateAllSwaps(ctx sdk.Context, cb func(swapped types.Swap) (stop bool)) {
   387  	store := ctx.KVStore(k.storeKey)
   388  	swapDataStore := prefix.NewStore(store, swapPrefix)
   389  
   390  	iterator := swapDataStore.Iterator(nil, nil)
   391  	defer iterator.Close()
   392  	for ; iterator.Valid(); iterator.Next() {
   393  		swap := types.Swap{}
   394  		k.cdc.MustUnmarshal(iterator.Value(), &swap)
   395  		if cb(swap) {
   396  			break
   397  		}
   398  	}
   399  }