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: adding ibc quota v2 #2296

Merged
merged 31 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
5578d8c
WIP: adding quota v2
gsk967 Oct 26, 2023
aeaf433
Merge branch 'main' into sai/quota_v2
gsk967 Oct 27, 2023
8663c38
adding inflow quota check
gsk967 Oct 27, 2023
fa816a8
trying to fix ibc tests
gsk967 Oct 27, 2023
39d21a7
Merge branch 'main' into sai/quota_v2
gsk967 Oct 31, 2023
f2f4660
update the protos
gsk967 Oct 31, 2023
49112a7
enable goconst in golang-lint
gsk967 Oct 31, 2023
b1382bf
address the review comments
gsk967 Oct 31, 2023
5ad6cb2
add new params values to upgrade handler
gsk967 Oct 31, 2023
0a7ad7e
Merge branch 'main' into sai/quota_v2
gsk967 Oct 31, 2023
53513de
Merge branch 'main' into sai/quota_v2
gsk967 Nov 3, 2023
0676c7f
address the second review comments
gsk967 Nov 3, 2023
aa7e8fb
removed denom metadata tracker from docs
gsk967 Nov 3, 2023
bad905b
Merge branch 'main' into sai/quota_v2
gsk967 Nov 4, 2023
6868b11
Merge branch 'main' into sai/quota_v2
gsk967 Nov 6, 2023
3f28e31
add check for token outflows with InflowOutflowQuotaTokenBase
gsk967 Nov 7, 2023
3b73075
fix the ibc e2e tests
gsk967 Nov 8, 2023
f40788c
Merge branch 'main' into sai/quota_v2
gsk967 Nov 13, 2023
c4f2a2b
Merge branch 'main' into sai/quota_v2
gsk967 Nov 14, 2023
1842dc0
Merge branch 'main' into sai/quota_v2
gsk967 Nov 14, 2023
c6dd61b
move the new migration of uibc params to keeper
gsk967 Nov 14, 2023
dd7e859
Merge branch 'main' into sai/quota_v2
gsk967 Nov 14, 2023
4d4ca41
make keys functions to private
gsk967 Nov 14, 2023
7478cac
Merge remote-tracking branch 'origin/sai/quota_v2' into sai/quota_v2
gsk967 Nov 14, 2023
e3359d6
add tests for uibc quota inflows
gsk967 Nov 14, 2023
66bc6a6
Merge branch 'main' into sai/quota_v2
gsk967 Nov 14, 2023
d90b400
refactored GetAllInflows and GetAllOutflows funs
gsk967 Nov 14, 2023
4d3c278
Update util/store/iter.go
robert-zaremba Nov 14, 2023
b2acff9
Merge branch 'main' into sai/quota_v2
gsk967 Nov 15, 2023
1edf594
fix the build issue
gsk967 Nov 15, 2023
19aea15
refactor LoadAllDecCoins func
gsk967 Nov 15, 2023
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
9 changes: 9 additions & 0 deletions app/upgrades.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (

"github.com/umee-network/umee/v6/util"
leveragetypes "github.com/umee-network/umee/v6/x/leverage/types"
"github.com/umee-network/umee/v6/x/uibc"
)

// RegisterUpgradeHandlersregisters upgrade handlers.
Expand Down Expand Up @@ -117,6 +118,14 @@ func (app *UmeeApp) registerUpgrade6_2(upgradeInfo upgradetypes.Plan) {
govParams := app.GovKeeper.GetParams(ctx)
govParams.MinInitialDepositRatio = sdk.NewDecWithPrec(1, 1).String()
err = app.GovKeeper.SetParams(ctx, govParams)
if err != nil {
return fromVM, err
}

// uibc migrations
uIBCKeeper := app.UIbcQuotaKeeperB.Keeper(&ctx)
uIBCKeeper.MigrateTotalOutflowSum()
err = uIBCKeeper.SetParams(uibc.DefaultParams())
return fromVM, err
},
)
Expand Down
12 changes: 11 additions & 1 deletion proto/umee/uibc/v1/genesis.proto
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ message GenesisState {
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins",
(gogoproto.nullable) = false
];

// total_outflow_sum defines the total outflow sum of ibc-transfer in USD.
string total_outflow_sum = 3 [
(cosmos_proto.scalar) = "cosmos.Dec",
Expand All @@ -31,4 +30,15 @@ message GenesisState {
(gogoproto.jsontag) = "quota_duration,omitempty",
(gogoproto.moretags) = "yaml:\"quota_expires\""
];
// inflows tracks IBC inflow transfers (in USD) for each denom during quota period.
repeated cosmos.base.v1beta1.DecCoin inflows = 5 [
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins",
(gogoproto.nullable) = false
];
// total_inflow_sum defines tracks total sum of IBC inflow transfers (in USD) during quota period.
string total_inflow_sum = 6 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
}
18 changes: 18 additions & 0 deletions proto/umee/uibc/v1/quota.proto
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,24 @@ message Params {
(gogoproto.jsontag) = "quota_duration,omitempty",
(gogoproto.moretags) = "yaml:\"quota_duration\""
];
// inflow_outflow_quota_base defines the inflow outflow quota base of ibc-transfer in USD
robert-zaremba marked this conversation as resolved.
Show resolved Hide resolved
string inflow_outflow_quota_base = 5 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
// inflow_outflow_quota_rate defines the rate of total inflows
string inflow_outflow_quota_rate = 6 [
(cosmos_proto.scalar) = "cosmos.Dec",
gsk967 marked this conversation as resolved.
Show resolved Hide resolved
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
// inflow_outflow_quota_token_base defines the inflow outflow quota base for token
string inflow_outflow_quota_token_base = 7 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
gsk967 marked this conversation as resolved.
Show resolved Hide resolved
}

// IBCTransferStatus status of ibc-transfer quota check for inflow and outflow
Expand Down
4 changes: 2 additions & 2 deletions tests/e2e/e2e_ibc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,8 @@ func (s *E2ETest) TestIBCTokenTransfer() {
// supply will be not be decreased because sending more than total quota from umee to gaia
s.checkSupply(umeeAPIEndpoint, uatomIBCHash, atomFromGaia.Amount)

// ✅ << BELOW TOKEN QUTOA 5$ but ATOM_QUOTA (5$)+ UMEE_QUOTA(90$) <= TOTAL QUOTA (120$) >>
// send $15 ATOM from umee to gaia
// ✅ << BELOW TOKEN QUTOA 5$ but ATOM_QUOTA (5$)+ UMEE_QUOTA(90$) <= TOTAL QUOTA (120$)
// send $5 ATOM from umee to gaia
sendAtom := mulCoin(atomQuota, "0.05")
s.SendIBC(s.Chain.ID, setup.GaiaChainID, "", sendAtom, false, "below both quotas")
// remaing supply decreased uatom on umee
Expand Down
15 changes: 15 additions & 0 deletions util/store/iter.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
func IteratePaginated(store sdk.KVStore, prefix []byte, page, limit uint, cb func(key, val []byte) error) error {
iter := sdk.KVStorePrefixIteratorPaginated(store, prefix, page, limit)
defer iter.Close()
return iterate(iter, cb)

Check warning on line 30 in util/store/iter.go

View check run for this annotation

Codecov / codecov/patch

util/store/iter.go#L30

Added line #L30 was not covered by tests
}

func iterate(iter db.Iterator, cb func(key, val []byte) error) error {
Expand All @@ -41,26 +41,26 @@
}

// LoadAll iterates over all records in the prefix store and unmarshals value into the list.
func LoadAll[TPtr PtrMarshalable[T], T any](s storetypes.KVStore, prefix []byte) ([]T, error) {
iter := sdk.KVStorePrefixIterator(s, prefix)
defer iter.Close()
out := make([]T, 0)
for ; iter.Valid(); iter.Next() {
var o TPtr = new(T)
if err := o.Unmarshal(iter.Value()); err != nil {
return nil, err
}
out = append(out, *o)

Check warning on line 53 in util/store/iter.go

View check run for this annotation

Codecov / codecov/patch

util/store/iter.go#L44-L53

Added lines #L44 - L53 were not covered by tests
}

return out, nil

Check warning on line 56 in util/store/iter.go

View check run for this annotation

Codecov / codecov/patch

util/store/iter.go#L56

Added line #L56 was not covered by tests
}

// MustLoadAll executes LoadAll and panics on error
func MustLoadAll[TPtr PtrMarshalable[T], T any](s storetypes.KVStore, prefix []byte) []T {
ls, err := LoadAll[TPtr, T](s, prefix)
util.Panic(err)
return ls

Check warning on line 63 in util/store/iter.go

View check run for this annotation

Codecov / codecov/patch

util/store/iter.go#L60-L63

Added lines #L60 - L63 were not covered by tests
}

// SumCoins aggregates all coins saved as (denom: Int) pairs in store. Use store/prefix.NewStore
Expand All @@ -80,3 +80,18 @@
// StrExtractor is a function type which will take a bytes string value and extracts
// string out of it.
type StrExtractor func([]byte) string

// LoadAllDecCoins iterates over all records in the prefix store and unmarshals value into the dec coin list.
func LoadAllDecCoins(iter db.Iterator, prefixLen int) (sdk.DecCoins, error) {
var coins sdk.DecCoins
for ; iter.Valid(); iter.Next() {
key, val := iter.Key(), iter.Value()
o := sdk.DecCoin{Denom: string(key[prefixLen:])}
if err := o.Amount.Unmarshal(val); err != nil {
return nil, err
}
coins = append(coins, o)

Check warning on line 93 in util/store/iter.go

View check run for this annotation

Codecov / codecov/patch

util/store/iter.go#L85-L93

Added lines #L85 - L93 were not covered by tests
}

return coins, nil

Check warning on line 96 in util/store/iter.go

View check run for this annotation

Codecov / codecov/patch

util/store/iter.go#L96

Added line #L96 was not covered by tests
}
9 changes: 9 additions & 0 deletions util/store/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,3 +209,12 @@ func GetInteger[T Integer](store sdk.KVStore, key []byte) (T, bool) {
}
panic("not possible: all types must be covered above")
}

// DeleteByPrefixStore will delete all keys stored in prefix store
func DeleteByPrefixStore(store sdk.KVStore) {
iter := sdk.KVStorePrefixIterator(store, nil)
defer iter.Close()
gsk967 marked this conversation as resolved.
Show resolved Hide resolved
for ; iter.Valid(); iter.Next() {
store.Delete(iter.Key())
}
}
26 changes: 9 additions & 17 deletions x/uibc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,12 @@

The `x/uibc` is a Cosmos Module providing:

- IBC Denom Metadata Tracker for [ICS-20](https://github.com/cosmos/ibc/tree/main/spec/app/ics-020-fungible-token-transfer) transferred tokens to backfill denom metadata into the x/bank standard Cosmos SDK module.
- IBC Quota is an ICS-4 middleware for the ICS-20 token transfer app to apply quota mechanism.

## Content

- [IBC Denom Metadata Tracker](#ibc-denom-metadata-tracker)
- [IBC Quota](#ibc-quota)

## IBC Denom Metadata Tracker

`x/bank.types.Metadata` is a structure which provides essential information about denom, such as display denom name, description, symbol, list of units (unit name and decimal exponent), and the default unit (`Base`).

ICS-20 is a x/bank token transfer protocol over IBC.
The core implementation doesn't create bank `Metadata` when a new token is transferred for the very first time. It's worth to note that token received through IBC is identified by the port ID, channel ID and the source denom ID.
The purpose of the `x/uibc/ics20` module is to wrap the core IBC module and create a denom `Metadata` whenever it is missing. Look at the [`TrackDenomMetadata`](ics20/keeper/keeper.go) function for more details.

### Considerations

The IBC ICS-20 doesn't carry any metadata information, so we only fill up the base denom. Importantly, we don't know about the `Exponent`, and we set `Exponent := 0`. In many cases this is wrong, and should be overwritten by chain governance.

## IBC Quota

Hack or lending abuse is impossible to stop once the funds leave the chain. One mitigation is to limit the IBC inflows and outflows and be able to stop a chain and recover the funds with a migration.
Expand All @@ -43,9 +29,9 @@ All outflows are measured in token average USD value using our x/oracle `AvgKeep

We define 2 Quotas for ICS-20 transfers. Each quota only tracks tokens x/leverage Token Registry.

- `Params.TokenQuota`: upper limit of a sum of all outflows per token. Initially it's set to 0.6m USD per token. It limits the outflows value for each token.
- `Params.TokenQuota`: upper limit of a sum of all outflows per token. It's set to 1.2M USD per token. It limits the outflows value for each token.
NOTE: we measure per token as defined in the x/leverage, not the IBC Denom Path (there can be multiple paths). Since creating a channel is permission less, we want to use same quota token.
- `Params.TotalQuota`: upper limit of a sum of all token outflows combined. Initially it's set to 1m USD. Example of IBC outflows reaching the total quota: 300k USD worth of ATOM, 200k USD worth of STATOM, 250k USD worth of UMEE and 250k USD worth JUNO.
- `Params.TotalQuota`: upper limit of a sum of all token outflows combined. For example if it's set to 1.6M USD then IBC outflows reaching the total quota will be 600k USD worth of ATOM, 500k USD worth of STATOM, 250k USD worth of UMEE and 250k USD worth JUNO.

If a quota parameter is set to zero then we consider it as unlimited.

Expand All @@ -57,7 +43,11 @@ Transfer of tokens, which are not registered in the x/leverage Token Registry ar

#### Inflows

We only allow inflows of tokens registered in x/leverage Token Registry. Other inflow transfers will be rejected.
All inflows are measured in token average USD value using our x/oracle `AvgKeeper`. The `AvgKeeper` aggregates TVWAP prices over 16h window.
We are only tracking inflows for tokens which are registered in x/leverage Token Registry.

- `Genesis.TotalInflowSum` : Sum of all IBC Tokens Inflows which are registered in x/leverage Token Registry.
- `Genesis.Inflows`: IBC Inflow of each registered token.

#### ICS-20 Quota control

Expand All @@ -75,6 +65,8 @@ In the state we store:
- Running sum of total outflow values, serialized as `sdk.Dec`.
- Running sum of per token outflow values, serialized as `sdk.Dec`.
- Next quota expire time (after which the quota reset happens).
- Running sum of total inflow values, serialized as `sdk.Dec`.
- Running sum of per token inflow values, serialized as `sdk.Dec`.

### Messages

Expand Down
15 changes: 15 additions & 0 deletions x/uibc/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ func NewGenesisState(params Params, outflows sdk.DecCoins, outflowSum sdk.Dec) *
func DefaultGenesisState() *GenesisState {
return &GenesisState{
Params: DefaultParams(),
Inflows: nil,
Outflows: nil,
TotalOutflowSum: sdk.NewDec(0),
TotalInflowSum: sdk.NewDec(0),
}
}

Expand All @@ -38,9 +40,22 @@ func (gs GenesisState) Validate() error {
}
}

for _, o := range gs.Inflows {
if o.Amount.IsNil() {
return sdkerrors.ErrInvalidRequest.Wrap("ibc denom inflow must be defined")
}
if err := o.Validate(); err != nil {
return err
}
}

if gs.TotalOutflowSum.IsNegative() {
return fmt.Errorf("total outflow sum cannot be negative : %s ", gs.TotalOutflowSum.String())
}

if gs.TotalInflowSum.IsNegative() {
return fmt.Errorf("total inflow sum cannot be negative : %s ", gs.TotalInflowSum.String())
}

return nil
}
Loading
Loading