Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basket module changes #596

Closed
wants to merge 9 commits into from
3 changes: 2 additions & 1 deletion RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
Features:

- Resolve claim undelegation issue
- Basket module enhancement for #441 #590 #591 #592 #594
- Spending module enhancement for #597
29 changes: 19 additions & 10 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,36 +271,45 @@ func NewInitApp(
app.TokensKeeper = tokenskeeper.NewKeeper(keys[tokenstypes.ModuleName], appCodec)
app.CustomGovKeeper = customgovkeeper.NewKeeper(keys[govtypes.ModuleName], appCodec, app.BankKeeper)
customStakingKeeper := customstakingkeeper.NewKeeper(keys[stakingtypes.ModuleName], cdc, app.CustomGovKeeper)
app.MultiStakingKeeper = multistakingkeeper.NewKeeper(keys[multistakingtypes.ModuleName], appCodec, app.BankKeeper, app.TokensKeeper, app.CustomGovKeeper, customStakingKeeper)
app.CustomSlashingKeeper = customslashingkeeper.NewKeeper(
multiStakingKeeper := multistakingkeeper.NewKeeper(keys[multistakingtypes.ModuleName], appCodec, app.BankKeeper, app.TokensKeeper, app.CustomGovKeeper, customStakingKeeper)
customSlashingKeeper := customslashingkeeper.NewKeeper(
appCodec,
keys[slashingtypes.StoreKey],
&customStakingKeeper,
app.MultiStakingKeeper,
multiStakingKeeper,
app.CustomGovKeeper,
app.GetSubspace(slashingtypes.ModuleName),
)
app.SpendingKeeper = spendingkeeper.NewKeeper(keys[spendingtypes.ModuleName], appCodec, app.BankKeeper, app.CustomGovKeeper)
// NOTE: customStakingKeeper above is passed by reference, so that it will contain these hooks
app.CustomStakingKeeper = *customStakingKeeper.SetHooks(
stakingtypes.NewMultiStakingHooks(app.CustomSlashingKeeper.Hooks()),
)

app.BasketKeeper = basketkeeper.NewKeeper(
keys[baskettypes.ModuleName], appCodec,
app.AccountKeeper, app.BankKeeper,
app.CustomGovKeeper,
app.MultiStakingKeeper,
app.TokensKeeper,
multiStakingKeeper,
)

app.CustomSlashingKeeper = *customSlashingKeeper.SetHooks(
slashingtypes.NewMultiSlashingHooks(app.BasketKeeper.Hooks()),
)

app.SpendingKeeper = spendingkeeper.NewKeeper(keys[spendingtypes.ModuleName], appCodec, app.BankKeeper, app.CustomGovKeeper)
// NOTE: customStakingKeeper above is passed by reference, so that it will contain these hooks
app.CustomStakingKeeper = *customStakingKeeper.SetHooks(
stakingtypes.NewMultiStakingHooks(app.CustomSlashingKeeper.Hooks()),
)

app.CollectivesKeeper = collectiveskeeper.NewKeeper(
keys[collectivestypes.StoreKey], appCodec,
app.BankKeeper,
app.CustomGovKeeper,
app.MultiStakingKeeper,
multiStakingKeeper,
app.TokensKeeper,
app.SpendingKeeper,
)
app.MultiStakingKeeper = *multiStakingKeeper.SetHooks(
multistakingtypes.NewMultiStakingHooks(app.BasketKeeper.Hooks()),
)

app.Layer2Keeper = layer2keeper.NewKeeper(
keys[collectivestypes.StoreKey], appCodec,
Expand Down
8 changes: 5 additions & 3 deletions proto/kira/basket/genesis.proto
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ import "kira/basket/basket.proto";
message GenesisState {
// baskets registered on the module
repeated Basket baskets = 1 [ (gogoproto.nullable) = false ];
// last basket id
uint64 last_basket_id = 2;
// mints by time
repeated AmountAtTime historical_mints = 2 [ (gogoproto.nullable) = false ];
repeated AmountAtTime historical_mints = 3 [ (gogoproto.nullable) = false ];
// burns by time
repeated AmountAtTime historical_burns = 3 [ (gogoproto.nullable) = false ];
repeated AmountAtTime historical_burns = 4 [ (gogoproto.nullable) = false ];
// swaps by time
repeated AmountAtTime historical_swaps = 4 [ (gogoproto.nullable) = false ];
repeated AmountAtTime historical_swaps = 5 [ (gogoproto.nullable) = false ];
}

// https://www.notion.so/kira-network/KIP-78-Token-Basketing-Module-7e6da1f2667c4c13b2274d546031d5db
10 changes: 8 additions & 2 deletions proto/kira/spending/pool.proto
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,19 @@ message WeightedRole {
option (gogoproto.equal) = true;

uint64 role = 1;
uint64 weight = 2;
string weight = 2 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
}
message WeightedAccount {
option (gogoproto.equal) = true;

string account = 1;
uint64 weight = 2;
string weight = 2 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
}
message WeightedPermInfo {
option (gogoproto.equal) = true;
Expand Down
2 changes: 1 addition & 1 deletion types/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ package types
const (
// we set page iteration limit for safety
PageIterationLimit = 512
SekaiVersion = "v0.3.36"
SekaiVersion = "v0.3.37"
CosmosVersion = "v0.47.5"
)
15 changes: 14 additions & 1 deletion x/basket/keeper/basket.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,15 @@ func (k Keeper) EditBasket(ctx sdk.Context, basket types.Basket) error {
}

usedDenom := make(map[string]bool)
rates, _ := basket.RatesAndIndexes()
basketDenomSupplyEst := sdk.ZeroDec()
for index, token := range basket.Tokens {
// ensure tokens amount is derivated from previous by denom
basket.Tokens[index].Amount = prevAmounts[token.Denom]
if !prevAmounts[token.Denom].IsNil() {
basket.Tokens[index].Amount = prevAmounts[token.Denom]
} else {
basket.Tokens[index].Amount = sdk.ZeroInt()
}

// validate denom for the token
if err := sdk.ValidateDenom(token.Denom); err != nil {
Expand All @@ -140,6 +146,13 @@ func (k Keeper) EditBasket(ctx sdk.Context, basket types.Basket) error {
return types.ErrDuplicateDenomExistsOnTokens
}
usedDenom[token.Denom] = true
basketDenomSupplyEst = basketDenomSupplyEst.
Add(basket.Tokens[index].Amount.ToLegacyDec().Mul(rates[token.Denom]))
}

supply := k.bk.GetSupply(ctx, basket.GetBasketDenom())
if supply.Amount.GT(basketDenomSupplyEst.TruncateInt()) {
return types.ErrBasketDenomSupplyTooBig
}

k.SetBasket(ctx, basket)
Expand Down
132 changes: 132 additions & 0 deletions x/basket/keeper/hooks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package keeper

import (
"fmt"

"github.com/KiraCore/sekai/x/basket/types"
multistakingtypes "github.com/KiraCore/sekai/x/multistaking/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)

func (k Keeper) AfterUpsertStakingPool(ctx sdk.Context, valAddr sdk.ValAddress, pool multistakingtypes.StakingPool) {
rates := k.tk.GetAllTokenRates(ctx)
for _, rate := range rates {
if rate.StakeToken {
basket, err := k.GetBasketByDenom(ctx, fmt.Sprintf("sdb/%s", rate.Denom))
if err != nil {
basket = types.Basket{
Id: 1,
Suffix: fmt.Sprintf("staking/%s", rate.Denom),
Description: fmt.Sprintf("Basket of staking derivatives for %s token", rate.Denom),
Amount: sdk.ZeroInt(),
SwapFee: sdk.ZeroDec(),
SlipppageFeeMin: sdk.ZeroDec(),
TokensCap: sdk.ZeroDec(),
LimitsPeriod: 86400,
MintsMin: sdk.OneInt(),
MintsMax: sdk.NewInt(1000_000_000_000), // 1M
MintsDisabled: false,
BurnsMin: sdk.OneInt(),
BurnsMax: sdk.NewInt(1000_000_000_000), // 1M
BurnsDisabled: false,
SwapsMin: sdk.OneInt(),
SwapsMax: sdk.NewInt(1000_000_000_000), // 1M
SwapsDisabled: false,
Tokens: []types.BasketToken{},
Surplus: []sdk.Coin{},
}
k.SetBasket(ctx, basket)
}

shareDenom := multistakingtypes.GetShareDenom(pool.Id, rate.Denom)
tokenMap := make(map[string]bool)
for _, token := range basket.Tokens {
tokenMap[token.Denom] = true
}
if !tokenMap[shareDenom] {
basket.Tokens = append(basket.Tokens, types.BasketToken{
Denom: shareDenom,
Weight: sdk.OneDec(),
Amount: sdk.ZeroInt(),
Deposits: true,
Withdraws: true,
Swaps: true,
})
}
}
}
}

func (k Keeper) AfterSlashStakingPool(ctx sdk.Context, valAddr sdk.ValAddress, pool multistakingtypes.StakingPool, slash sdk.Dec) {
rates := k.tk.GetAllTokenRates(ctx)
for _, rate := range rates {
if rate.StakeToken {
basket, err := k.GetBasketByDenom(ctx, fmt.Sprintf("sdb/%s", rate.Denom))
if err != nil {
continue
}

shareDenom := multistakingtypes.GetShareDenom(pool.Id, rate.Denom)
for i, token := range basket.Tokens {
if token.Denom == shareDenom {
basket.Tokens[i].Weight = token.Weight.Mul(sdk.OneDec().Sub(slash))
basket.Tokens[i].Deposits = true
basket.Tokens[i].Withdraws = true
basket.Tokens[i].Swaps = true
}
}
k.SetBasket(ctx, basket)
}
}
}

func (k Keeper) AfterSlashProposalRaise(ctx sdk.Context, valAddr sdk.ValAddress, pool multistakingtypes.StakingPool) {
rates := k.tk.GetAllTokenRates(ctx)
for _, rate := range rates {
if rate.StakeToken {
basket, err := k.GetBasketByDenom(ctx, fmt.Sprintf("sdb/%s", rate.Denom))
if err != nil {
continue
}

shareDenom := multistakingtypes.GetShareDenom(pool.Id, rate.Denom)
for i, token := range basket.Tokens {
if token.Denom == shareDenom {
basket.Tokens[i].Deposits = false
basket.Tokens[i].Withdraws = false
basket.Tokens[i].Swaps = false
}
}
k.SetBasket(ctx, basket)
}
}
}

//_________________________________________________________________________________________

// Hooks wrapper struct for multistaking keeper
type Hooks struct {
k Keeper
}

var _ types.MultistakingHooks = Hooks{}

// Return the wrapper struct
func (k Keeper) Hooks() Hooks {
return Hooks{k}
}

// Implements Multistaking hooks
func (h Hooks) AfterUpsertStakingPool(ctx sdk.Context, valAddr sdk.ValAddress, pool multistakingtypes.StakingPool) {
h.k.AfterUpsertStakingPool(ctx, valAddr, pool)
}

// Implements Multistaking hooks
func (h Hooks) AfterSlashStakingPool(ctx sdk.Context, valAddr sdk.ValAddress, pool multistakingtypes.StakingPool, slash sdk.Dec) {
h.k.AfterSlashStakingPool(ctx, valAddr, pool, slash)
}

// Implements Slashing hooks
func (h Hooks) AfterSlashProposalRaise(ctx sdk.Context, valAddr sdk.ValAddress, pool multistakingtypes.StakingPool) {
h.k.AfterSlashProposalRaise(ctx, valAddr, pool)
}
5 changes: 4 additions & 1 deletion x/basket/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/KiraCore/sekai/x/basket/types"
govkeeper "github.com/KiraCore/sekai/x/gov/keeper"
govtypes "github.com/KiraCore/sekai/x/gov/types"
tokenskeeper "github.com/KiraCore/sekai/x/tokens/keeper"
"github.com/cosmos/cosmos-sdk/codec"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand All @@ -16,17 +17,19 @@ type Keeper struct {
ak types.AccountKeeper
bk types.BankKeeper
gk govkeeper.Keeper
tk tokenskeeper.Keeper
mk types.MultiStakingKeeper
}

// NewKeeper returns instance of a keeper
func NewKeeper(storeKey storetypes.StoreKey, cdc codec.BinaryCodec, ak types.AccountKeeper, bk types.BankKeeper, gk govkeeper.Keeper, mk types.MultiStakingKeeper) Keeper {
func NewKeeper(storeKey storetypes.StoreKey, cdc codec.BinaryCodec, ak types.AccountKeeper, bk types.BankKeeper, gk govkeeper.Keeper, tk tokenskeeper.Keeper, mk types.MultiStakingKeeper) Keeper {
return Keeper{
cdc: cdc,
storeKey: storeKey,
ak: ak,
bk: bk,
gk: gk,
tk: tk,
mk: mk,
}
}
Expand Down
18 changes: 7 additions & 11 deletions x/basket/keeper/mint_burn_swap.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"github.com/KiraCore/sekai/x/basket/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
)

func (k Keeper) MintBasketToken(ctx sdk.Context, msg *types.MsgBasketTokenMint) error {
Expand Down Expand Up @@ -47,7 +46,7 @@ func (k Keeper) MintBasketToken(ctx sdk.Context, msg *types.MsgBasketTokenMint)
basketTokenAmount = basketTokenAmount.Add(sdk.NewDecFromInt(token.Amount).Mul(rate))
}

basketCoin := sdk.NewCoin(basket.GetBasketDenom(), basketTokenAmount.RoundInt())
basketCoin := sdk.NewCoin(basket.GetBasketDenom(), basketTokenAmount.TruncateInt())

if basketCoin.Amount.LT(basket.MintsMin) {
return types.ErrAmountBelowBaksetMintsMin
Expand Down Expand Up @@ -133,7 +132,7 @@ func (k Keeper) BurnBasketToken(ctx sdk.Context, msg *types.MsgBasketTokenBurn)
if !token.Withdraws {
continue
}
withdrawAmount := sdk.NewDecFromInt(token.Amount).Mul(portion).RoundInt()
withdrawAmount := sdk.NewDecFromInt(token.Amount).Mul(portion).TruncateInt()
if withdrawAmount.IsPositive() {
withdrawCoins = withdrawCoins.Add(sdk.NewCoin(token.Denom, withdrawAmount))
}
Expand Down Expand Up @@ -211,7 +210,7 @@ func (k Keeper) BasketSwap(ctx sdk.Context, msg *types.MsgBasketTokenSwap) error
return types.ErrSwapsDisabledForOutToken
}

swapValue := sdk.NewDecFromInt(pair.InAmount.Amount).Mul(inRate).RoundInt()
swapValue := sdk.NewDecFromInt(pair.InAmount.Amount).Mul(inRate).TruncateInt()
if swapValue.LT(basket.SwapsMin) {
return types.ErrAmountBelowBaksetSwapsMin
}
Expand All @@ -223,18 +222,15 @@ func (k Keeper) BasketSwap(ctx sdk.Context, msg *types.MsgBasketTokenSwap) error
}

// calculate out amount considering fees and rates
swapAmount := sdk.NewDecFromInt(pair.InAmount.Amount).Mul(sdk.OneDec().Sub(basket.SwapFee)).RoundInt()
swapAmount := sdk.NewDecFromInt(pair.InAmount.Amount).Mul(sdk.OneDec().Sub(basket.SwapFee)).TruncateInt()

// pay network for fee
feeAmount := pair.InAmount.Amount.Sub(swapAmount)
if feeAmount.IsPositive() {
err := k.bk.SendCoinsFromModuleToModule(ctx, types.ModuleName, authtypes.FeeCollectorName, sdk.Coins{sdk.NewCoin(pair.InAmount.Denom, feeAmount)})
if err != nil {
return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, err.Error())
}
basket.Surplus = sdk.Coins(basket.Surplus).Add(sdk.NewCoin(pair.InAmount.Denom, feeAmount))
}

outAmount := sdk.NewDecFromInt(swapAmount).Mul(inRate).Quo(outRate).RoundInt()
outAmount := sdk.NewDecFromInt(swapAmount).Mul(inRate).Quo(outRate).TruncateInt()
if outAmount.IsZero() {
return types.ErrNotAbleToWithdrawAnyTokens
}
Expand All @@ -259,7 +255,7 @@ func (k Keeper) BasketSwap(ctx sdk.Context, msg *types.MsgBasketTokenSwap) error
slippageFee := basket.SlippageFee(oldDisbalance)
finalOutCoins := sdk.Coins{}
for _, coin := range outAmounts {
finalOutAmount := sdk.NewDecFromInt(coin.Amount).Mul(sdk.OneDec().Sub(slippageFee)).RoundInt()
finalOutAmount := sdk.NewDecFromInt(coin.Amount).Mul(sdk.OneDec().Sub(slippageFee)).TruncateInt()
finalOutCoins = finalOutCoins.Add(sdk.NewCoin(coin.Denom, finalOutAmount))
}
err = k.bk.SendCoinsFromModuleToAccount(ctx, types.ModuleName, sender, finalOutCoins)
Expand Down
8 changes: 1 addition & 7 deletions x/basket/keeper/mint_burn_swap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"github.com/KiraCore/sekai/x/basket/types"
"github.com/cometbft/cometbft/crypto/ed25519"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
)

Expand Down Expand Up @@ -645,7 +644,7 @@ func (suite *KeeperTestSuite) TestBasketSwap() {
prevSwapAmount: sdk.NewInt(0),
tokensCap: sdk.NewDec(1),
expectErr: false,
expectedOutAmount: sdk.NewCoins(sdk.NewInt64Coin("ueth", 8_920)),
expectedOutAmount: sdk.NewCoins(sdk.NewInt64Coin("ueth", 8_919)),
},
}

Expand Down Expand Up @@ -759,11 +758,6 @@ func (suite *KeeperTestSuite) TestBasketSwap() {
historicalAmount := suite.app.BasketKeeper.GetLimitsPeriodSwapAmount(suite.ctx, 1, tc.limitPeriod)
suite.Require().Equal(historicalAmount, tc.prevSwapAmount.Add(tc.swapBalance.Amount))

// check correct fee amount
feeCollector := suite.app.AccountKeeper.GetModuleAddress(authtypes.FeeCollectorName)
feeCollectorBalance := suite.app.BankKeeper.GetBalance(suite.ctx, feeCollector, "ukex")
suite.Require().Equal(feeCollectorBalance.Amount, sdk.NewDecFromInt(tc.swapBalance.Amount).Mul(basket.SwapFee).RoundInt())

// check correct slippage amount + surplus
suite.Require().True(sdk.Coins(savedBasket.Surplus).Sub(basket.Surplus...).IsAllPositive())
}
Expand Down
Loading
Loading