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

feat: emergency group index update #2246

Merged
merged 12 commits into from
Sep 27, 2023
1 change: 1 addition & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,7 @@ func New(
app.BankKeeper,
app.LeverageKeeper,
app.OracleKeeper,
app.UGovKeeperB.EmergencyGroup,
)
}

Expand Down
8 changes: 8 additions & 0 deletions util/checkers/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package checkers

func Merge(errs1, errs2 []error) []error {
if len(errs2) > 0 {
return append(errs1, errs2...)
}
return errs1
}
6 changes: 6 additions & 0 deletions x/metoken/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/tendermint/tendermint/libs/log"
"github.com/umee-network/umee/v6/x/ugov"

"github.com/umee-network/umee/v6/x/metoken"
)
Expand All @@ -16,6 +17,7 @@ type Builder struct {
bankKeeper metoken.BankKeeper
leverageKeeper metoken.LeverageKeeper
oracleKeeper metoken.OracleKeeper
ugov ugov.EmergencyGroupBuilder
}

// NewKeeperBuilder returns Builder object.
Expand All @@ -25,13 +27,15 @@ func NewKeeperBuilder(
bankKeeper metoken.BankKeeper,
leverageKeeper metoken.LeverageKeeper,
oracleKeeper metoken.OracleKeeper,
ugov ugov.EmergencyGroupBuilder,
) Builder {
return Builder{
cdc: cdc,
storeKey: storeKey,
bankKeeper: bankKeeper,
leverageKeeper: leverageKeeper,
oracleKeeper: oracleKeeper,
ugov: ugov,
}
}

Expand All @@ -41,6 +45,7 @@ type Keeper struct {
bankKeeper metoken.BankKeeper
leverageKeeper metoken.LeverageKeeper
oracleKeeper metoken.OracleKeeper
ugov ugov.EmergencyGroupBuilder

// TODO: ctx should be removed when we migrate leverageKeeper and oracleKeeper
ctx *sdk.Context
Expand All @@ -54,6 +59,7 @@ func (b Builder) Keeper(ctx *sdk.Context) Keeper {
bankKeeper: b.bankKeeper,
leverageKeeper: b.leverageKeeper,
oracleKeeper: b.oracleKeeper,
ugov: b.ugov,
ctx: ctx,
}
}
Expand Down
217 changes: 147 additions & 70 deletions x/metoken/keeper/metoken.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package keeper

import (
"errors"
"fmt"
"time"

sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"

"github.com/umee-network/umee/v6/util/checkers"
"github.com/umee-network/umee/v6/util/store"
"github.com/umee-network/umee/v6/x/metoken"
)
Expand Down Expand Up @@ -69,10 +71,7 @@ func (k Keeper) setNextInterestClaimTime(nextInterestClaimTime time.Time) {
}

// UpdateIndexes registers `addIndexes` and processes `updateIndexes` to update existing indexes.
robert-zaremba marked this conversation as resolved.
Show resolved Hide resolved
func (k Keeper) UpdateIndexes(
addIndexes []metoken.Index,
updateIndexes []metoken.Index,
) error {
func (k Keeper) UpdateIndexes(toAdd, toUpdate []metoken.Index, byEmergencyGroup bool) error {
registry := k.GetAllRegisteredIndexes()

registeredIndexes := make(map[string]metoken.Index)
Expand All @@ -84,57 +83,64 @@ func (k Keeper) UpdateIndexes(
}
}

if err := k.addIndexes(addIndexes, registeredIndexes, registeredAssets); err != nil {
return err
var errs []error
if byEmergencyGroup {
if len(toAdd) > 0 {
errs = append(errs, sdkerrors.ErrInvalidRequest.Wrapf("Emergency Group cannot register new indexes"))
}

errs = checkers.Merge(errs, validateEGIndexUpdate(toUpdate, registeredIndexes))

if len(errs) > 0 {
return errors.Join(errs...)
}
}

return k.updateIndexes(updateIndexes, registeredIndexes, registeredAssets)
errs = checkers.Merge(errs, k.addIndexes(toAdd, registeredIndexes, registeredAssets))
errs = checkers.Merge(errs, k.updateIndexes(toUpdate, registeredIndexes, registeredAssets))

return errors.Join(errs...)
}

// addIndexes handles addition of the indexes from the request along with their validations.
func (k Keeper) addIndexes(
indexes []metoken.Index,
registeredIndexes map[string]metoken.Index,
registeredAssets map[string]string,
) error {
) []error {
var allErrs []error
for _, index := range indexes {
var indexErrs []error
if _, present := registeredIndexes[index.Denom]; present {
return sdkerrors.ErrInvalidRequest.Wrapf(
"add: index with denom %s already exists",
index.Denom,
allErrs = append(
allErrs, sdkerrors.ErrInvalidRequest.Wrapf(
"add: index with denom %s already exists",
index.Denom,
),
)
continue
}

var errs []error
for _, aa := range index.AcceptedAssets {
if _, present := registeredAssets[aa.Denom]; present {
errs = append(
errs, sdkerrors.ErrInvalidRequest.Wrapf(
"add: asset %s is already accepted in another index",
aa.Denom,
),
)
}
}

if len(errs) != 0 {
return errors.Join(errs...)
if exists := k.hasIndexBalance(index.Denom); exists {
allErrs = append(
allErrs, sdkerrors.ErrInvalidRequest.Wrapf(
"can't add index %s - it already exists and is active",
index.Denom,
),
)
}

if err := k.validateInLeverage(index); err != nil {
return err
}
indexErrs = checkers.Merge(indexErrs, checkDuplicatedAssets(index.AcceptedAssets, registeredAssets, ""))
indexErrs = checkers.Merge(indexErrs, k.validateInLeverage(index))

if exists := k.hasIndexBalance(index.Denom); exists {
return sdkerrors.ErrInvalidRequest.Wrapf(
"can't add index %s - it already exists and is active",
index.Denom,
)
if len(indexErrs) > 0 {
allErrs = append(allErrs, indexErrs...)
continue
}

// adding index
if err := k.setRegisteredIndex(index); err != nil {
return err
return []error{err}
}

assetBalances := make([]metoken.AssetBalance, 0)
Expand All @@ -151,10 +157,14 @@ func (k Keeper) addIndexes(
), assetBalances,
),
); err != nil {
return err
return []error{err}
}
}

if len(allErrs) != 0 {
return allErrs
}

return nil
}

Expand All @@ -163,65 +173,66 @@ func (k Keeper) updateIndexes(
indexes []metoken.Index,
registeredIndexes map[string]metoken.Index,
registeredAssets map[string]string,
) error {
) []error {
var allErrs []error
for _, index := range indexes {
var indexErrs []error
kosegor marked this conversation as resolved.
Show resolved Hide resolved
oldIndex, present := registeredIndexes[index.Denom]
if !present {
return sdkerrors.ErrNotFound.Wrapf(
"update: index with denom %s not found",
index.Denom,
allErrs = append(
allErrs, sdkerrors.ErrNotFound.Wrapf(
"update: index with denom %s not found",
index.Denom,
),
)
continue
}

var errs []error
for _, aa := range index.AcceptedAssets {
if indexDenom, present := registeredAssets[aa.Denom]; present && indexDenom != index.Denom {
errs = append(
errs, sdkerrors.ErrInvalidRequest.Wrapf(
"add: asset %s is already accepted in another index",
aa.Denom,
),
)
}
}

if len(errs) != 0 {
return errors.Join(errs...)
}
indexErrs = checkers.Merge(
indexErrs,
checkDuplicatedAssets(index.AcceptedAssets, registeredAssets, index.Denom),
)

if oldIndex.Exponent != index.Exponent {
balances, err := k.IndexBalances(index.Denom)
if err != nil {
return err
return []error{err}
}

if balances.MetokenSupply.IsPositive() {
return sdkerrors.ErrInvalidRequest.Wrapf(
"update: index %s exponent cannot be changed when supply is greater than zero",
index.Denom,
indexErrs = append(
indexErrs, sdkerrors.ErrInvalidRequest.Wrapf(
"update: index %s exponent cannot be changed when supply is greater than zero",
index.Denom,
),
)
}
}

for _, aa := range oldIndex.AcceptedAssets {
if exists := index.HasAcceptedAsset(aa.Denom); !exists {
return sdkerrors.ErrInvalidRequest.Wrapf(
"update: an asset %s cannot be deleted from an index %s",
aa.Denom,
index.Denom,
indexErrs = append(
indexErrs, sdkerrors.ErrInvalidRequest.Wrapf(
"update: an asset %s cannot be deleted from an index %s",
aa.Denom,
index.Denom,
),
)
}
}

if err := k.validateInLeverage(index); err != nil {
return err
indexErrs = checkers.Merge(indexErrs, k.validateInLeverage(index))

if len(indexErrs) > 0 {
allErrs = append(allErrs, indexErrs...)
continue
}

// updating balances if there is a new accepted asset
if len(index.AcceptedAssets) > len(oldIndex.AcceptedAssets) {
balances, err := k.IndexBalances(index.Denom)
if err != nil {
return err
return []error{err}
}

for _, aa := range index.AcceptedAssets {
Expand All @@ -231,27 +242,93 @@ func (k Keeper) updateIndexes(
}

if err := k.setIndexBalances(balances); err != nil {
return err
return []error{err}
}
}

if err := k.setRegisteredIndex(index); err != nil {
return err
return []error{err}
}
}

if len(allErrs) != 0 {
return allErrs
}

return nil
}

func checkDuplicatedAssets(
assets []metoken.AcceptedAsset,
registeredAssets map[string]string,
indexDenom string,
) []error {
var errs []error
for _, aa := range assets {
if d, present := registeredAssets[aa.Denom]; present && (len(indexDenom) == 0 || d != indexDenom) {
errs = append(
errs,
sdkerrors.ErrInvalidRequest.Wrapf(
"add: asset %s is already accepted in another index %s",
aa.Denom,
d,
),
)
}
}

return errs
}

// validateEGIndexUpdate checks if emergency group can perform updates.
func validateEGIndexUpdate(indexes []metoken.Index, registeredIndexes map[string]metoken.Index) []error {
var errs []error
for _, newIndex := range indexes {
d := newIndex.Denom
oldIndex, ok := registeredIndexes[d]
if !ok {
errs = append(errs, sdkerrors.ErrNotFound.Wrapf("update: index with denom %s not found", d))
continue
}

if newIndex.Exponent != oldIndex.Exponent {
errs = append(errs, errors.New(d+": exponent cannot be changed"))
}

if !newIndex.Fee.Equal(oldIndex.Fee) {
errs = append(errs, errors.New(d+": fee cannot be changed"))
}

if !newIndex.MaxSupply.Equal(oldIndex.MaxSupply) {
errs = append(errs, errors.New(d+": max_supply cannot be changed"))
}

for _, newAsset := range newIndex.AcceptedAssets {
oldAsset, i := oldIndex.AcceptedAsset(newAsset.Denom)
if i < 0 {
errs = append(errs, fmt.Errorf("%s: new asset %s cannot be added", d, newAsset.Denom))
continue
}

if !newAsset.ReservePortion.Equal(oldAsset.ReservePortion) {
errs = append(errs, fmt.Errorf("%s: reserve_portion of %s cannot be changed", d, newAsset.Denom))
}
}
}

return errs
}

// validateInLeverage validate the existence of every accepted asset in x/leverage
func (k Keeper) validateInLeverage(index metoken.Index) error {
func (k Keeper) validateInLeverage(index metoken.Index) []error {
var errs []error
for _, aa := range index.AcceptedAssets {
if _, err := k.leverageKeeper.GetTokenSettings(*k.ctx, aa.Denom); err != nil {
return err
errs = append(errs, err)
}
}

return nil
return errs
}

func ModuleAddr() sdk.AccAddress {
Expand Down
Loading
Loading