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: quota zero check #1973

Merged
merged 5 commits into from
Apr 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,13 @@ Ref: https://keepachangelog.com/en/1.0.0/
- [1963](https://github.com/umee-network/umee/pull/1963) ICA Host integration.
- [1953](https://github.com/umee-network/umee/pull/1953) IBC: accept only inflow of leverage registered tokens
- [1967](https://github.com/umee-network/umee/pull/1967) Gravity Bridge phase out phase-2: disable Umee -> Ethereum transfers.
- [1967](https://github.com/umee-network/umee/pull/1967) Gravity Bridge phase out phase-2: disable Umee -> Ethereum transfers.

### Improvements

- [1959](https://github.com/umee-network/umee/pull/1959) Update IBC to v6.1
- [1913](https://github.com/umee-network/umee/pull/1913), [1974](https://github.com/umee-network/umee/pull/1974) uibc: quota status check.
- [1973](https://github.com/umee-network/umee/pull/1973) UIBC: handle zero Quota Params.

### Fixes

Expand Down
1 change: 0 additions & 1 deletion x/uibc/ics20/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ func (k Keeper) Transfer(goCtx context.Context, msg *ibctransfertypes.MsgTransfe
}

return resp, err

}

// OnRecvPacket delegates the OnRecvPacket call to the embedded ICS-20 transfer
Expand Down
2 changes: 1 addition & 1 deletion x/uibc/quota/keeper/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ func (k Keeper) InitGenesis(ctx sdk.Context, genState uibc.GenesisState) {
err := k.SetParams(ctx, genState.Params)
util.Panic(err)

k.SetOutflows(ctx, genState.Outflows)
k.SetTokenOutflows(ctx, genState.Outflows)
k.SetTotalOutflowSum(ctx, genState.TotalOutflowSum)

err = k.SetExpire(ctx, genState.QuotaExpires)
Expand Down
2 changes: 1 addition & 1 deletion x/uibc/quota/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func (q Querier) Outflows(goCtx context.Context, req *uibc.QueryOutflows) (
if len(req.Denom) == 0 {
o = q.GetTotalOutflow(ctx)
} else {
d, err := q.GetOutflows(ctx, req.Denom)
d, err := q.GetTokenOutflows(ctx, req.Denom)
if err != nil {
return nil, err
}
Expand Down
10 changes: 0 additions & 10 deletions x/uibc/quota/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (

"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/simapp"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
"github.com/cosmos/cosmos-sdk/x/staking"
Expand All @@ -18,7 +17,6 @@ import (

umeeapp "github.com/umee-network/umee/v4/app"
appparams "github.com/umee-network/umee/v4/app/params"
"github.com/umee-network/umee/v4/tests/tsdk"
"github.com/umee-network/umee/v4/x/uibc"
"github.com/umee-network/umee/v4/x/uibc/quota/keeper"
)
Expand Down Expand Up @@ -88,11 +86,3 @@ func initKeeperTestSuite(t *testing.T) *KeeperTestSuite {

return s
}

// creates keeper without external dependencies (app, leverage etc...)
func initSimpleKeeper(t *testing.T) (sdk.Context, keeper.Keeper) {
storeKey := storetypes.NewMemoryStoreKey("quota")
k := keeper.NewKeeper(nil, storeKey, nil, nil, nil)
ctx, _ := tsdk.NewCtxOneStore(t, storeKey)
return ctx, k
}
56 changes: 56 additions & 0 deletions x/uibc/quota/keeper/mocks_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package keeper

import (
"errors"

sdk "github.com/cosmos/cosmos-sdk/types"

ltypes "github.com/umee-network/umee/v4/x/leverage/types"
)

type LeverageKeeper struct {
tokenSettings map[string]ltypes.Token
}

func (k LeverageKeeper) GetTokenSettings(ctx sdk.Context, baseDenom string) (ltypes.Token, error) {
ts, ok := k.tokenSettings[baseDenom]
if !ok {
return ts, errors.New("token settings not found")
}
return ts, nil
}
func (k LeverageKeeper) ExchangeUToken(ctx sdk.Context, uToken sdk.Coin) (sdk.Coin, error) {
panic("not implemented")
}
func (k LeverageKeeper) DeriveExchangeRate(ctx sdk.Context, denom string) sdk.Dec {
panic("not implemented")
}

func NewLeverageKeeperMock(denoms ...string) LeverageKeeper {
tokenSettings := map[string]ltypes.Token{}
for _, d := range denoms {
tokenSettings[d] = ltypes.Token{
BaseDenom: d,
SymbolDenom: d,
}
}
return LeverageKeeper{tokenSettings: tokenSettings}
}

type Oracle struct {
prices map[string]sdk.Dec
}

func (o Oracle) Price(ctx sdk.Context, denom string) (sdk.Dec, error) {
p, ok := o.prices[denom]
if !ok {
return p, ltypes.ErrNotRegisteredToken.Wrap(denom)
}
return p, nil
}

func NewOracleMock(denom string, price sdk.Dec) Oracle {
prices := map[string]sdk.Dec{}
prices[denom] = price
return Oracle{prices: prices}
}
43 changes: 21 additions & 22 deletions x/uibc/quota/keeper/quota.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (

var ten = sdk.MustNewDecFromStr("10")

// GetAllOutflows returns outflows of all tokens.
// GetAllOutflows returns sum of outflows of all tokens in USD value.
func (k Keeper) GetAllOutflows(ctx sdk.Context) (sdk.DecCoins, error) {
var outflows sdk.DecCoins
store := k.PrefixStore(&ctx, uibc.KeyPrefixDenomOutflows)
Expand All @@ -33,29 +33,30 @@ func (k Keeper) GetAllOutflows(ctx sdk.Context) (sdk.DecCoins, error) {
return outflows, nil
}

// GetOutflows retunes the rate limits of ibc denom.
func (k Keeper) GetOutflows(ctx sdk.Context, ibcDenom string) (sdk.DecCoin, error) {
// GetTokenOutflows returns sum of denom outflows in USD value in the DecCoin structure.
func (k Keeper) GetTokenOutflows(ctx sdk.Context, denom string) (sdk.DecCoin, error) {
store := ctx.KVStore(k.storeKey)
bz := store.Get(uibc.KeyTotalOutflows(ibcDenom))
bz := store.Get(uibc.KeyTotalOutflows(denom))
if bz == nil {
return coin.ZeroDec(ibcDenom), nil
return coin.ZeroDec(denom), nil
}

var d sdk.Dec
err := d.Unmarshal(bz)

return sdk.NewDecCoinFromDec(ibcDenom, d), err
return sdk.NewDecCoinFromDec(denom, d), err
}

// SetOutflows save the updated IBC outflows of by provided tokens.
func (k Keeper) SetOutflows(ctx sdk.Context, outflows sdk.DecCoins) {
// SetTokenOutflows saves provided updated IBC outflows as a pair: USD value, denom name in the
// DecCoin structure.
func (k Keeper) SetTokenOutflows(ctx sdk.Context, outflows sdk.DecCoins) {
for _, q := range outflows {
k.SetDenomOutflow(ctx, q)
k.SetTokenOutflow(ctx, q)
}
}

// SetDenomOutflow save the outflows of denom into store.
func (k Keeper) SetDenomOutflow(ctx sdk.Context, outflow sdk.DecCoin) {
// SetTokenOutflow save the outflows of denom into store.
func (k Keeper) SetTokenOutflow(ctx sdk.Context, outflow sdk.DecCoin) {
gsk967 marked this conversation as resolved.
Show resolved Hide resolved
store := ctx.KVStore(k.storeKey)
key := uibc.KeyTotalOutflows(outflow.Denom)
bz, err := outflow.Amount.Marshal()
Expand Down Expand Up @@ -131,12 +132,6 @@ func (k Keeper) ResetAllQuotas(ctx sdk.Context) error {
// updates the current quota metrics.
func (k Keeper) CheckAndUpdateQuota(ctx sdk.Context, denom string, newOutflow sdkmath.Int) error {
params := k.GetParams(ctx)

o, err := k.GetOutflows(ctx, denom)
if err != nil {
return err
}

exchangePrice, err := k.getExchangePrice(ctx, denom, newOutflow)
if err != nil {
if ltypes.ErrNotRegisteredToken.Is(err) {
Expand All @@ -146,17 +141,21 @@ func (k Keeper) CheckAndUpdateQuota(ctx sdk.Context, denom string, newOutflow sd
}
}

o, err := k.GetTokenOutflows(ctx, denom)
if err != nil {
return err
}
o.Amount = o.Amount.Add(exchangePrice)
if o.Amount.GT(params.TokenQuota) {
if !params.TokenQuota.IsZero() && o.Amount.GT(params.TokenQuota) {
return uibc.ErrQuotaExceeded
}

totalOutflowSum := k.GetTotalOutflow(ctx).Add(exchangePrice)
if totalOutflowSum.GT(params.TotalQuota) {
if !params.TotalQuota.IsZero() && totalOutflowSum.GT(params.TotalQuota) {
return uibc.ErrQuotaExceeded
}

k.SetDenomOutflow(ctx, o)
k.SetTokenOutflow(ctx, o)
k.SetTotalOutflowSum(ctx, totalOutflowSum)
return nil
}
Expand Down Expand Up @@ -193,7 +192,7 @@ func (k Keeper) getExchangePrice(ctx sdk.Context, denom string, amount sdkmath.I

// UndoUpdateQuota subtracts `amount` from quota metric of the ibc denom.
func (k Keeper) UndoUpdateQuota(ctx sdk.Context, denom string, amount sdkmath.Int) error {
o, err := k.GetOutflows(ctx, denom)
o, err := k.GetTokenOutflows(ctx, denom)
if err != nil {
return err
}
Expand All @@ -215,7 +214,7 @@ func (k Keeper) UndoUpdateQuota(ctx sdk.Context, denom string, amount sdkmath.In
return nil
}

k.SetDenomOutflow(ctx, o)
k.SetTokenOutflow(ctx, o)

totalOutflowSum := k.GetTotalOutflow(ctx)
k.SetTotalOutflowSum(ctx, totalOutflowSum.Sub(exchangePrice))
Expand Down
33 changes: 3 additions & 30 deletions x/uibc/quota/keeper/quota_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,50 +5,23 @@ import (

sdk "github.com/cosmos/cosmos-sdk/types"
"gotest.tools/v3/assert"

ibcutil "github.com/umee-network/umee/v4/util/ibc"
)

func TestGetQuotas(t *testing.T) {
ctx, k := initSimpleKeeper(t)

quotas, err := k.GetAllOutflows(ctx)
assert.NilError(t, err)
assert.Equal(t, len(quotas), 0)

setQuotas := sdk.DecCoins{sdk.NewInt64DecCoin("test_uumee", 10000)}

k.SetOutflows(ctx, setQuotas)
quotas, err = k.GetAllOutflows(ctx)
assert.NilError(t, err)
assert.DeepEqual(t, setQuotas, quotas)

// get the quota of denom
quota, err := k.GetOutflows(ctx, setQuotas[0].Denom)
assert.NilError(t, err)
assert.Equal(t, quota.Denom, setQuotas[0].Denom)
}

func TestGetLocalDenom(t *testing.T) {
out := ibcutil.GetLocalDenom("umee")
assert.Equal(t, "umee", out)
}

func TestResetQuota(t *testing.T) {
s := initKeeperTestSuite(t)
ctx, k := s.ctx, s.app.UIbcQuotaKeeper

umeeQuota := sdk.NewInt64DecCoin("uumee", 1000)
k.SetDenomOutflow(ctx, umeeQuota)
q, err := k.GetOutflows(ctx, umeeQuota.Denom)
k.SetTokenOutflow(ctx, umeeQuota)
q, err := k.GetTokenOutflows(ctx, umeeQuota.Denom)
assert.NilError(t, err)
assert.DeepEqual(t, q, umeeQuota)

// reset the quota
k.ResetAllQuotas(ctx)

// check the quota after reset
q, err = k.GetOutflows(ctx, umeeQuota.Denom)
q, err = k.GetTokenOutflows(ctx, umeeQuota.Denom)
assert.NilError(t, err)
assert.DeepEqual(t, q.Amount, sdk.NewDec(0))
}
108 changes: 108 additions & 0 deletions x/uibc/quota/keeper/quota_unit_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package keeper

import (
"testing"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"

ibcutil "github.com/umee-network/umee/v4/util/ibc"
)

func TestGetQuotas(t *testing.T) {
k := initUmeeKeeper(t)
ctx := *k.ctx

quotas, err := k.GetAllOutflows(ctx)
require.NoError(t, err)
require.Equal(t, len(quotas), 0)

setQuotas := sdk.DecCoins{sdk.NewInt64DecCoin("test_uumee", 10000)}

k.SetTokenOutflows(ctx, setQuotas)
quotas, err = k.GetAllOutflows(ctx)
require.NoError(t, err)
require.Equal(t, setQuotas, quotas)

// get the quota of denom
quota, err := k.GetTokenOutflows(ctx, setQuotas[0].Denom)
require.NoError(t, err)
require.Equal(t, quota.Denom, setQuotas[0].Denom)
}

func TestGetLocalDenom(t *testing.T) {
out := ibcutil.GetLocalDenom("umee")
require.Equal(t, "umee", out)
}

func TestCheckAndUpdateQuota(t *testing.T) {
k := initUmeeKeeper(t)

// initUmeeKeeper sets umee price: 2usd

// 1. We set the quota param to 10 and existing outflow sum to 6 USD.
// Transfer of 2 USD in Umee should work, but additional transfer of 4 USD should fail.
//
k.setQuotaParams(10, 100)
k.SetTokenOutflow(*k.ctx, sdk.NewInt64DecCoin(umee, 6))
k.SetTotalOutflowSum(*k.ctx, sdk.NewDec(50))

err := k.CheckAndUpdateQuota(*k.ctx, umee, sdk.NewInt(1))
require.NoError(t, err)
k.checkOutflows(umee, 8, 52)

// transferring 2 umee => 4USD, will exceed the quota (8+4 > 10)
err = k.CheckAndUpdateQuota(*k.ctx, umee, sdk.NewInt(2))
require.ErrorContains(t, err, "quota")
k.checkOutflows(umee, 8, 52)

// transferring 1 umee => 2USD, will will be still OK (8+2 <= 10)
err = k.CheckAndUpdateQuota(*k.ctx, umee, sdk.NewInt(1))
require.NoError(t, err)
k.checkOutflows(umee, 10, 54)

// 2. Setting TokenQuota param to 0 should unlimit the token quota check
//
k.setQuotaParams(0, 100)

// transferring 20 umee => 40USD, will skip the token quota check, but will update outflows
err = k.CheckAndUpdateQuota(*k.ctx, umee, sdk.NewInt(20))
require.NoError(t, err)
k.checkOutflows(umee, 50, 94)

// transferring additional 5 umee => 10USD, will fail total quota check
err = k.CheckAndUpdateQuota(*k.ctx, umee, sdk.NewInt(5))
require.ErrorContains(t, err, "quota")
k.checkOutflows(umee, 50, 94)

// 3. Setting TotalQuota param to 0 should unlimit the total quota check
//
k.setQuotaParams(0, 0)
err = k.CheckAndUpdateQuota(*k.ctx, umee, sdk.NewInt(5))
require.NoError(t, err)
k.checkOutflows(umee, 60, 104)

// 4. Setting TokenQuota to 65
//
k.setQuotaParams(65, 0)
err = k.CheckAndUpdateQuota(*k.ctx, umee, sdk.NewInt(1))
require.NoError(t, err)
k.checkOutflows(umee, 62, 106)

err = k.CheckAndUpdateQuota(*k.ctx, umee, sdk.NewInt(2)) // exceeds token quota
require.ErrorContains(t, err, "quota")
}

func TestGetExchangePrice(t *testing.T) {
k := initUmeeKeeper(t)
p, err := k.getExchangePrice(*k.ctx, umee, sdk.NewInt(12))
require.NoError(t, err)
require.Equal(t, sdk.NewDec(24), p)

p, err = k.getExchangePrice(*k.ctx, atom, sdk.NewInt(3))
require.NoError(t, err)
require.Equal(t, sdk.NewDec(30), p)

_, err = k.getExchangePrice(*k.ctx, "notexisting", sdk.NewInt(10))
require.ErrorContains(t, err, "not found")
}
Loading