github.com/KiraCore/sekai@v0.3.43/x/basket/keeper/basket.go (about)

     1  package keeper
     2  
     3  import (
     4  	"github.com/KiraCore/sekai/x/basket/types"
     5  	sdk "github.com/cosmos/cosmos-sdk/types"
     6  	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
     7  )
     8  
     9  func (k Keeper) GetLastBasketId(ctx sdk.Context) uint64 {
    10  	store := ctx.KVStore(k.storeKey)
    11  	bz := store.Get(types.KeyLastBasketId)
    12  	if bz == nil {
    13  		return 0
    14  	}
    15  	return sdk.BigEndianToUint64(bz)
    16  }
    17  
    18  func (k Keeper) SetLastBasketId(ctx sdk.Context, id uint64) {
    19  	idBz := sdk.Uint64ToBigEndian(id)
    20  	store := ctx.KVStore(k.storeKey)
    21  	store.Set(types.KeyLastBasketId, idBz)
    22  }
    23  
    24  func (k Keeper) GetBasketById(ctx sdk.Context, id uint64) (types.Basket, error) {
    25  	store := ctx.KVStore(k.storeKey)
    26  	bz := store.Get(append(types.PrefixBasketKey, sdk.Uint64ToBigEndian(id)...))
    27  	if bz == nil {
    28  		return types.Basket{}, sdkerrors.Wrapf(types.ErrBasketDoesNotExist, "basket: %d does not exist", id)
    29  	}
    30  	basket := types.Basket{}
    31  	k.cdc.MustUnmarshal(bz, &basket)
    32  	return basket, nil
    33  }
    34  
    35  func (k Keeper) GetBasketByDenom(ctx sdk.Context, denom string) (types.Basket, error) {
    36  	store := ctx.KVStore(k.storeKey)
    37  	bz := store.Get(append(types.PrefixBasketByDenomKey, denom...))
    38  	if bz == nil {
    39  		return types.Basket{}, sdkerrors.Wrapf(types.ErrBasketDoesNotExist, "basket: %s does not exist", denom)
    40  	}
    41  	id := sdk.BigEndianToUint64(bz)
    42  	return k.GetBasketById(ctx, id)
    43  }
    44  
    45  func (k Keeper) SetBasket(ctx sdk.Context, basket types.Basket) {
    46  	idBz := sdk.Uint64ToBigEndian(basket.Id)
    47  	bz := k.cdc.MustMarshal(&basket)
    48  	store := ctx.KVStore(k.storeKey)
    49  	store.Set(append(types.PrefixBasketKey, idBz...), bz)
    50  	store.Set(append(types.PrefixBasketByDenomKey, basket.GetBasketDenom()...), idBz)
    51  }
    52  
    53  func (k Keeper) DeleteBasket(ctx sdk.Context, basket types.Basket) {
    54  	idBz := sdk.Uint64ToBigEndian(basket.Id)
    55  	store := ctx.KVStore(k.storeKey)
    56  	store.Delete(append(types.PrefixBasketKey, idBz...))
    57  	store.Delete(append(types.PrefixBasketByDenomKey, basket.GetBasketDenom()...))
    58  }
    59  
    60  func (k Keeper) GetAllBaskets(ctx sdk.Context) []types.Basket {
    61  	store := ctx.KVStore(k.storeKey)
    62  
    63  	baskets := []types.Basket{}
    64  	it := sdk.KVStorePrefixIterator(store, types.PrefixBasketKey)
    65  	defer it.Close()
    66  
    67  	for ; it.Valid(); it.Next() {
    68  		basket := types.Basket{}
    69  		k.cdc.MustUnmarshal(it.Value(), &basket)
    70  		baskets = append(baskets, basket)
    71  	}
    72  	return baskets
    73  }
    74  
    75  func (k Keeper) CreateBasket(ctx sdk.Context, basket types.Basket) error {
    76  	// create a new basket id
    77  	basketId := k.GetLastBasketId(ctx) + 1
    78  	k.SetLastBasketId(ctx, basketId)
    79  	basket.Id = basketId
    80  	basket.Surplus = sdk.Coins{} // surplus is zero at initial
    81  
    82  	if len(basket.Tokens) == 0 {
    83  		return types.ErrEmptyUnderlyingTokens
    84  	}
    85  	usedDenom := make(map[string]bool)
    86  	for index, token := range basket.Tokens {
    87  		// ensure tokens amount is zero
    88  		basket.Tokens[index].Amount = sdk.ZeroInt()
    89  		// validate denom for the token
    90  		if err := sdk.ValidateDenom(token.Denom); err != nil {
    91  			return err
    92  		}
    93  		if token.Weight.IsZero() {
    94  			return types.ErrTokenWeightShouldNotBeZero
    95  		}
    96  		if usedDenom[token.Denom] {
    97  			return types.ErrDuplicateDenomExistsOnTokens
    98  		}
    99  		usedDenom[token.Denom] = true
   100  	}
   101  
   102  	k.SetBasket(ctx, basket)
   103  	return nil
   104  }
   105  
   106  func (k Keeper) EditBasket(ctx sdk.Context, basket types.Basket) error {
   107  	oldBasket, err := k.GetBasketById(ctx, basket.Id)
   108  	if err != nil {
   109  		return err
   110  	}
   111  
   112  	// TODO: what happens if suffix change?
   113  	// TODO: what happens if a basket token is removed?
   114  
   115  	// use previous surplus
   116  	basket.Surplus = oldBasket.Surplus
   117  
   118  	prevAmounts := make(map[string]sdk.Int)
   119  	for _, token := range oldBasket.Tokens {
   120  		prevAmounts[token.Denom] = token.Amount
   121  	}
   122  
   123  	if len(basket.Tokens) == 0 {
   124  		return types.ErrEmptyUnderlyingTokens
   125  	}
   126  
   127  	usedDenom := make(map[string]bool)
   128  	rates, _ := basket.RatesAndIndexes()
   129  	basketDenomSupplyEst := sdk.ZeroDec()
   130  	for index, token := range basket.Tokens {
   131  		// ensure tokens amount is derivated from previous by denom
   132  		if !prevAmounts[token.Denom].IsNil() {
   133  			basket.Tokens[index].Amount = prevAmounts[token.Denom]
   134  		} else {
   135  			basket.Tokens[index].Amount = sdk.ZeroInt()
   136  		}
   137  
   138  		// validate denom for the token
   139  		if err := sdk.ValidateDenom(token.Denom); err != nil {
   140  			return err
   141  		}
   142  		if token.Weight.IsZero() {
   143  			return types.ErrTokenWeightShouldNotBeZero
   144  		}
   145  		if usedDenom[token.Denom] {
   146  			return types.ErrDuplicateDenomExistsOnTokens
   147  		}
   148  		usedDenom[token.Denom] = true
   149  		basketDenomSupplyEst = basketDenomSupplyEst.
   150  			Add(basket.Tokens[index].Amount.ToLegacyDec().Mul(rates[token.Denom]))
   151  	}
   152  
   153  	supply := k.bk.GetSupply(ctx, basket.GetBasketDenom())
   154  	if supply.Amount.GT(basketDenomSupplyEst.TruncateInt()) {
   155  		return types.ErrBasketDenomSupplyTooBig
   156  	}
   157  
   158  	k.SetBasket(ctx, basket)
   159  	return nil
   160  }