Skip to content

Commit

Permalink
feat!: ibc transfer quota (#1568)
Browse files Browse the repository at this point in the history
* WIP: add ibc rate limit middleware to ibc transfer

* WIP: add cll tx,query cmds

1. add cli queries for rate_limits and params
2. add updateProposal for rate_limits and ibcPause status

* WIP: add check rate limits for ibc denom

* remove ibc-rate-limits and move rate limits into ibctransfer module

* move the umme ibctransfer into ics20 folder

* fix: fix the lint

* refactor: change the module structure to 'authz' type of cosmos-sdk

* remove inflow_limit in rate limits

* chore: check the rate limits by exchange rate

* add denom exponent to calculate amount in USD

* calculating the sent amount with exponent of registerd token

* fix: fix the amount calculation

* fix: fix the rate-limit reset issue

* refactor: update the params
  1. add token_quota, total_quota, quota_duration params
  2. ibc-transfer token quota checking with token_quota and total_quota params

* fix: fix the build

* chore: update the limits and quota interval

* chore: fix the lint issues

* chore: fix the lint issues

* chore: addree the pr comments
1. rename ibcratelimit to uibc module
2. update the uibc cli tx and queries
3. fix the quota expires of ibc-transfer

* chore: remove the param subspace from uibc

* refactor: refactor the proto and logic
1. ibc-transfer status now enum (enabled,disabled,paused)
2. get the exchange from base denom (ibc/xxx)

* chore: address the pr comments
1. remove expire time for quota records
2. removed params subspace keys

* fix: fix the buf lint

* chore: get the exchange price from TokenValue of leverage

* chore: convert uToken to baseToken in ibc-transfer quota checking

* chore: add typed events for emit events

* address the review comments
1. Rename ibc_denom to denom query

* refactor the code
1. rename the GovUpdateTransferStatus to GovSetIBCPause
2. address the pr comments

* address the pr comments

* chore: fix the build issue

* chore: add tests for params

* fix: fix the update quota params

* chore: address the pr comments

* chore: fix the lint

* address the pr comments++

* address the pr comments

* update reset quota

* move to Marshal to MustMarshal in uibc

* add TODO for ibc middleware acknowledgement

* add comment to outflow_sum

---------

Co-authored-by: Robert Zaremba <robert@zaremba.ch>
  • Loading branch information
gsk967 and robert-zaremba authored Jan 30, 2023
1 parent 6253a22 commit 5d1a0a3
Show file tree
Hide file tree
Showing 42 changed files with 5,713 additions and 68 deletions.
103 changes: 77 additions & 26 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,20 @@ import (
appparams "github.com/umee-network/umee/v4/app/params"
"github.com/umee-network/umee/v4/swagger"
"github.com/umee-network/umee/v4/util/genmap"
uibctransfer "github.com/umee-network/umee/v4/x/ibctransfer"
uibctransferkeeper "github.com/umee-network/umee/v4/x/ibctransfer/keeper"
"github.com/umee-network/umee/v4/x/leverage"
leveragekeeper "github.com/umee-network/umee/v4/x/leverage/keeper"
leveragetypes "github.com/umee-network/umee/v4/x/leverage/types"
"github.com/umee-network/umee/v4/x/oracle"
oraclekeeper "github.com/umee-network/umee/v4/x/oracle/keeper"
oracletypes "github.com/umee-network/umee/v4/x/oracle/types"

// umee ibc-transfer and quota for ibc-transfer
"github.com/umee-network/umee/v4/x/uibc"
uics20transfer "github.com/umee-network/umee/v4/x/uibc/ics20"
uibctransferkeeper "github.com/umee-network/umee/v4/x/uibc/ics20/keeper"
uibcmodule "github.com/umee-network/umee/v4/x/uibc/module"
uibcquota "github.com/umee-network/umee/v4/x/uibc/quota"
uibcquotakeeper "github.com/umee-network/umee/v4/x/uibc/quota/keeper"
)

var (
Expand Down Expand Up @@ -174,7 +180,7 @@ func init() {
}

if Experimental {
moduleBasics = append(moduleBasics, wasm.AppModuleBasic{})
moduleBasics = append(moduleBasics, wasm.AppModuleBasic{}, uibcmodule.AppModuleBasic{})
}

ModuleBasics = module.NewBasicManager(moduleBasics...)
Expand All @@ -196,6 +202,7 @@ func init() {

if Experimental {
maccPerms[wasm.ModuleName] = []string{authtypes.Burner}
maccPerms[uibc.ModuleName] = nil
}
}

Expand Down Expand Up @@ -241,6 +248,7 @@ type UmeeApp struct {
LeverageKeeper leveragekeeper.Keeper
OracleKeeper oraclekeeper.Keeper
bech32IbcKeeper bech32ibckeeper.Keeper
uibcQuotaKeeper uibcquotakeeper.Keeper

// make scoped keepers public for testing purposes
ScopedIBCKeeper capabilitykeeper.ScopedKeeper
Expand Down Expand Up @@ -300,12 +308,11 @@ func New(
govtypes.StoreKey, paramstypes.StoreKey, upgradetypes.StoreKey, feegrant.StoreKey,
evidencetypes.StoreKey, capabilitytypes.StoreKey,
authzkeeper.StoreKey, nftkeeper.StoreKey, group.StoreKey,
ibchost.StoreKey, ibctransfertypes.StoreKey,
gravitytypes.StoreKey,
ibchost.StoreKey, ibctransfertypes.StoreKey, gravitytypes.StoreKey,
leveragetypes.StoreKey, oracletypes.StoreKey, bech32ibctypes.StoreKey,
}
if Experimental {
storeKeys = append(storeKeys, wasm.StoreKey)
storeKeys = append(storeKeys, wasm.StoreKey, uibc.StoreKey)
}

keys := sdk.NewKVStoreKeys(storeKeys...)
Expand Down Expand Up @@ -442,18 +449,14 @@ func New(
app.StakingKeeper,
distrtypes.ModuleName,
)
var err error
app.LeverageKeeper, err = leveragekeeper.NewKeeper(
app.LeverageKeeper = leveragekeeper.NewKeeper(
appCodec,
keys[leveragetypes.ModuleName],
app.GetSubspace(leveragetypes.ModuleName),
app.BankKeeper,
app.OracleKeeper,
cast.ToBool(appOpts.Get(leveragetypes.FlagEnableLiquidatorQuery)),
)
if err != nil {
panic(err)
}
app.LeverageKeeper = *app.LeverageKeeper.SetHooks(
leveragetypes.NewMultiHooks(
app.OracleKeeper.Hooks(),
Expand Down Expand Up @@ -494,13 +497,35 @@ func New(
// If evidence needs to be handled for the app, set routes in router here and seal
app.EvidenceKeeper = *evidenceKeeper

app.IBCKeeper = ibckeeper.NewKeeper(
appCodec,
keys[ibchost.StoreKey],
app.GetSubspace(ibchost.ModuleName),
*app.StakingKeeper,
app.UpgradeKeeper,
app.ScopedIBCKeeper,
)

var ics4Wrapper ibcporttypes.ICS4Wrapper
if Experimental {
app.uibcQuotaKeeper = uibcquotakeeper.NewKeeper(
appCodec,
keys[uibc.StoreKey],
app.IBCKeeper.ChannelKeeper, app.LeverageKeeper,
)
ics4Wrapper = app.uibcQuotaKeeper
} else {
ics4Wrapper = app.IBCKeeper.ChannelKeeper
}

// Middleware Stacks
// Create an original ICS-20 transfer keeper and AppModule and then use it to
// created an Umee wrapped ICS-20 transfer keeper and AppModule.
ibcTransferKeeper := ibctransferkeeper.NewKeeper(
appCodec,
keys[ibctransfertypes.StoreKey],
app.GetSubspace(ibctransfertypes.ModuleName),
app.IBCKeeper.ChannelKeeper,
ics4Wrapper, // ISC4 Wrapper: IBC Rate Limit middleware
app.IBCKeeper.ChannelKeeper,
&app.IBCKeeper.PortKeeper,
app.AccountKeeper,
Expand All @@ -509,15 +534,34 @@ func New(
)

app.UIBCTransferKeeper = uibctransferkeeper.New(ibcTransferKeeper, app.BankKeeper)
ibcTransferModule := ibctransfer.NewAppModule(ibcTransferKeeper)
uibcTransferIBCModule := uibctransfer.NewIBCModule(
ibctransfer.NewIBCModule(ibcTransferKeeper), app.UIBCTransferKeeper,

// Create Transfer Stack
// SendPacket, since it is originating from the application to core IBC:
// transferKeeper.SendPacket -> uibcquota.SendPacket -> channel.SendPacket

// RecvPacket, message that originates from core IBC and goes down to app, the flow is the other way
// channel.RecvPacket -> uibcquota.OnRecvPacket -> transfer.OnRecvPacket

// transfer stack contains (from top to bottom):
// - Umee IBC Transfer
// - IBC Rate Limit Middleware

// create IBC module from bottom to top of stack
var transferStack ibcporttypes.IBCModule
transferStack = uics20transfer.NewIBCModule(
ibctransfer.NewIBCModule(ibcTransferKeeper),
app.UIBCTransferKeeper,
)

if Experimental {
transferStack = uibcquota.NewIBCMiddleware(transferStack, app.uibcQuotaKeeper)
}

// Create IBC Router
// create static IBC router, add transfer route, then set and seal it
ibcRouter := ibcporttypes.NewRouter()
ibcRouter.AddRoute(ibctransfertypes.ModuleName, uibcTransferIBCModule)

// Add transfer stack to IBC Router
ibcRouter.AddRoute(ibctransfertypes.ModuleName, transferStack)
ibcRouter.AddRoute(wasm.ModuleName, wasm.NewIBCHandler(app.WasmKeeper, app.IBCKeeper.ChannelKeeper))
app.IBCKeeper.SetRouter(ibcRouter)

Expand Down Expand Up @@ -593,15 +637,18 @@ func New(
groupmodule.NewAppModule(appCodec, app.GroupKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry),
nftmodule.NewAppModule(appCodec, app.NFTKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry),

ibcTransferModule,
ibctransfer.NewAppModule(ibcTransferKeeper),
gravity.NewAppModule(app.GravityKeeper, app.BankKeeper),
leverage.NewAppModule(appCodec, app.LeverageKeeper, app.AccountKeeper, app.BankKeeper),
oracle.NewAppModule(appCodec, app.OracleKeeper, app.AccountKeeper, app.BankKeeper),
bech32ibc.NewAppModule(appCodec, app.bech32IbcKeeper),
}
if Experimental {
appModules = append(appModules,
wasm.NewAppModule(app.appCodec, &app.WasmKeeper, app.StakingKeeper, app.AccountKeeper, app.BankKeeper))
appModules = append(
appModules,
wasm.NewAppModule(app.appCodec, &app.WasmKeeper, app.StakingKeeper, app.AccountKeeper, app.BankKeeper),
uibcmodule.NewAppModule(appCodec, app.uibcQuotaKeeper),
)
}

app.mm = module.NewManager(appModules...)
Expand All @@ -626,6 +673,7 @@ func New(
gravitytypes.ModuleName,
bech32ibctypes.ModuleName,
}

endBlockers := []string{
crisistypes.ModuleName,
oracletypes.ModuleName, // must be before gov and staking
Expand Down Expand Up @@ -661,6 +709,7 @@ func New(
gravitytypes.ModuleName,
bech32ibctypes.ModuleName,
}

orderMigrations := []string{
capabilitytypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, distrtypes.ModuleName,
stakingtypes.ModuleName, slashingtypes.ModuleName, govtypes.ModuleName, minttypes.ModuleName,
Expand All @@ -676,10 +725,10 @@ func New(
}

if Experimental {
beginBlockers = append(beginBlockers, wasm.ModuleName)
endBlockers = append(endBlockers, wasm.ModuleName)
initGenesis = append(initGenesis, wasm.ModuleName)
orderMigrations = append(orderMigrations, wasm.ModuleName)
beginBlockers = append(beginBlockers, wasm.ModuleName, uibc.ModuleName)
endBlockers = append(endBlockers, wasm.ModuleName, uibc.ModuleName)
initGenesis = append(initGenesis, wasm.ModuleName, uibc.ModuleName)
orderMigrations = append(orderMigrations, wasm.ModuleName, uibc.ModuleName)
}

app.mm.SetOrderBeginBlockers(beginBlockers...)
Expand All @@ -704,8 +753,10 @@ func New(
authtypes.ModuleName: auth.NewAppModule(app.appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts),
}

simStateModules := genmap.Pick(app.mm.Modules,
[]string{stakingtypes.ModuleName, authtypes.ModuleName, oracletypes.ModuleName})
simStateModules := genmap.Pick(
app.mm.Modules,
[]string{stakingtypes.ModuleName, authtypes.ModuleName, oracletypes.ModuleName},
)
// TODO: Ensure x/leverage implements simulator and add it here:
simTestModules := genmap.Pick(simStateModules, []string{oracletypes.ModuleName})

Expand Down
17 changes: 17 additions & 0 deletions proto/umee/uibc/v1/events.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
syntax = "proto3";
package umee.uibc.v1;

import "gogoproto/gogo.proto";

option go_package = "github.com/umee-network/umee/v4/x/uibc";
option (gogoproto.goproto_getters_all) = false;

// EventBadRevert is emitted on failure of ibc-transfer quota.
message EventBadRevert {
// module name
string module = 1;
// failure event type
string failure_type = 2;
// ibc packet data
string packet = 3;
}
29 changes: 29 additions & 0 deletions proto/umee/uibc/v1/genesis.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
syntax = "proto3";
package umee.uibc.v1;

import "gogoproto/gogo.proto";
import "cosmos_proto/cosmos.proto";
import "google/protobuf/timestamp.proto";
import "umee/uibc/v1/quota.proto";

option go_package = "github.com/umee-network/umee/v4/x/uibc";
option (gogoproto.goproto_getters_all) = false;

// GenesisState defines the uibc module's genesis state.
message GenesisState {
Params params = 1 [(gogoproto.nullable) = false];
repeated Quota quotas = 2 [(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",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
// quota_expires defines quota expires for ibc-transfer denom in seconds
google.protobuf.Timestamp quota_expires = 4 [
(gogoproto.nullable) = false,
(gogoproto.stdtime) = true,
(gogoproto.jsontag) = "quota_duration,omitempty",
(gogoproto.moretags) = "yaml:\"quota_expires\""
];
}
44 changes: 44 additions & 0 deletions proto/umee/uibc/v1/query.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
syntax = "proto3";
package umee.uibc.v1;

import "google/api/annotations.proto";
import "gogoproto/gogo.proto";
import "umee/uibc/v1/quota.proto";

option go_package = "github.com/umee-network/umee/v4/x/uibc";

option (gogoproto.goproto_getters_all) = false;

// Query defines the gRPC querier service.
service Query {
// Params queries the parameters of the x/uibc module.
rpc Params(QueryParams) returns (QueryParamsResponse) {
option (google.api.http).get = "/umee/uibc/v1/params";
}

// Quota queries the rate limits of ibc denoms.
// If denom is empty, returns quota for all tokens.
rpc Quota(QueryQuota) returns (QueryQuotaResponse) {
option (google.api.http).get = "/umee/uibc/v1/quota/{denom}";
}
}

// QueryParams defines the request structure for the Params gRPC service
// handler.
message QueryParams {}

// QueryParamsResponse defines the response structure for the Params gRPC
// service handler.
message QueryParamsResponse {
Params params = 1 [(gogoproto.nullable) = false];
}

// QueryQuota defines request type for query the quota of denoms
message QueryQuota {
string denom = 1;
}

// QueryQuotaResponse defines response type of Query/Quota
message QueryQuotaResponse {
repeated Quota quota = 1 [(gogoproto.nullable) = false];
}
57 changes: 57 additions & 0 deletions proto/umee/uibc/v1/quota.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
syntax = "proto3";
package umee.uibc.v1;

import "gogoproto/gogo.proto";
import "google/protobuf/duration.proto";
import "cosmos_proto/cosmos.proto";

option go_package = "github.com/umee-network/umee/v4/x/uibc";

// Quota stores current sum of IBC outflow transfers of IBC Denom.
message Quota {
// ibc_denom defines the ibc denom
string ibc_denom = 1;
// outflow_sum defines the sum of price (USD) value of outflow tokens through ibc-transfer
string outflow_sum = 3 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
}

// Params of x/uibc module
message Params {
// ibc_status defines the wethever ibc-transfer enabled, disbaled or paused
IBCTransferStatus ibc_pause = 1;
// total_quota defines the total outflow limit of ibc-transfer in USD
string total_quota = 2 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
// token_quota defines the outflow limit per token in USD
string token_quota = 3 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
// quota_duration defines quota expires for each ibc-transfer denom in seconds
google.protobuf.Duration quota_duration = 4 [
(gogoproto.nullable) = false,
(gogoproto.stdduration) = true,
(gogoproto.jsontag) = "quota_duration,omitempty",
(gogoproto.moretags) = "yaml:\"quota_duration\""
];
}

// IBCTransferStatus status of ibc-transfer
enum IBCTransferStatus {
// UNSPECIFIED defines a no-op status.
IBC_TRANSFER_STATUS_UNSPECIFIED = 0;
// DISABLED defines the quota checking diabled for ibc-transfer.
IBC_TRANSFER_STATUS_DISABLED = 1;
// ENABLED defines the enable quota checking for ibc-transfer.
IBC_TRANSFER_STATUS_ENABLED = 2;
// PAUSED defines pause the ibc-transfer from app.
IBC_TRANSFER_STATUS_PAUSED = 3;
}
Loading

0 comments on commit 5d1a0a3

Please sign in to comment.