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 }