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

[CORE-824] Implement keeper methods for LimitParams and DenomCapacity #877

Merged
merged 5 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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
4 changes: 3 additions & 1 deletion proto/dydxprotocol/ibcratelimit/limit_params.proto
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ message LimitParams {
string denom = 1;
// limiters is a list of rate-limiters on this denom. All limiters
// must be satified for a withdrawal to proceed.
repeated Limiter limiters = 2;
repeated Limiter limiters = 2 [
(gogoproto.nullable) = false
];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 NOTE
Codebase verification is a beta feature.

The review of the new context from the shell scripts indicates that the non-nullable change to the limiters field in the LimitParams message will have significant implications across the Go codebase. The following files and areas are particularly affected:

  • MsgSetLimitParams struct in tx.pb.go still references LimitParams as a pointer.
  • keeper.go and keeper_test.go contain logic and tests for setting and getting LimitParams.
  • params.go includes a Validate method for LimitParams.
  • genesis.pb.go sets up initial LimitParams in the genesis state.
  • query.pb.go involves listing LimitParams, which may affect marshaling and unmarshaling.

Given these findings, it is crucial to ensure that all parts of the codebase that interact with LimitParams and its limiters field are updated to handle the non-pointer slice correctly.

  • Update MsgSetLimitParams in tx.pb.go to reflect the non-pointer nature of LimitParams.
  • Review and update logic and tests in keeper.go and keeper_test.go for compatibility with the non-nullable limiters field.
  • Verify the Validate method in params.go for any adjustments needed due to the change.
  • Ensure that the genesis state initialization in genesis.pb.go correctly handles the updated LimitParams.
  • Check serialization and deserialization processes in query.pb.go to ensure they work with the non-nullable limiters field.
Analysis chain

The change to make the limiters field non-nullable is a significant alteration to the data structure. This will affect the generated Go code, potentially impacting initialization and usage patterns. Ensure that all parts of the codebase that interact with LimitParams and its limiters field are updated to handle the non-pointer slice correctly.

}

// Limiter defines one rate-limiter on a specfic denom.
Expand Down
21 changes: 21 additions & 0 deletions protocol/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ import (
feetiersmodule "github.com/dydxprotocol/v4-chain/protocol/x/feetiers"
feetiersmodulekeeper "github.com/dydxprotocol/v4-chain/protocol/x/feetiers/keeper"
feetiersmoduletypes "github.com/dydxprotocol/v4-chain/protocol/x/feetiers/types"
ibcratelimitmodulekeeper "github.com/dydxprotocol/v4-chain/protocol/x/ibcratelimit/keeper"
ibcratelimitmoduletypes "github.com/dydxprotocol/v4-chain/protocol/x/ibcratelimit/types"
perpetualsmodule "github.com/dydxprotocol/v4-chain/protocol/x/perpetuals"
perpetualsmodulekeeper "github.com/dydxprotocol/v4-chain/protocol/x/perpetuals/keeper"
perpetualsmoduletypes "github.com/dydxprotocol/v4-chain/protocol/x/perpetuals/types"
Expand All @@ -152,6 +154,7 @@ import (
rewardsmodule "github.com/dydxprotocol/v4-chain/protocol/x/rewards"
rewardsmodulekeeper "github.com/dydxprotocol/v4-chain/protocol/x/rewards/keeper"
rewardsmoduletypes "github.com/dydxprotocol/v4-chain/protocol/x/rewards/types"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: remove extra line

sendingmodule "github.com/dydxprotocol/v4-chain/protocol/x/sending"
sendingmodulekeeper "github.com/dydxprotocol/v4-chain/protocol/x/sending/keeper"
sendingmoduletypes "github.com/dydxprotocol/v4-chain/protocol/x/sending/types"
Expand Down Expand Up @@ -237,6 +240,7 @@ type App struct {
IBCKeeper *ibckeeper.Keeper
EvidenceKeeper evidencekeeper.Keeper
TransferKeeper ibctransferkeeper.Keeper
IBCRateLimitKeeper ibcratelimitmodulekeeper.Keeper
FeeGrantKeeper feegrantkeeper.Keeper
ConsensusParamsKeeper consensusparamkeeper.Keeper

Expand Down Expand Up @@ -343,6 +347,7 @@ func New(
distrtypes.StoreKey, slashingtypes.StoreKey,
govtypes.StoreKey, paramstypes.StoreKey, consensusparamtypes.StoreKey, upgradetypes.StoreKey, feegrant.StoreKey,
ibcexported.StoreKey, ibctransfertypes.StoreKey,
ibcratelimitmoduletypes.StoreKey,
evidencetypes.StoreKey,
capabilitytypes.StoreKey,
pricesmoduletypes.StoreKey,
Expand Down Expand Up @@ -533,6 +538,18 @@ func New(
transferModule := transfer.NewAppModule(app.TransferKeeper)
transferIBCModule := transfer.NewIBCModule(app.TransferKeeper)

app.IBCRateLimitKeeper = *ibcratelimitmodulekeeper.NewKeeper(
appCodec,
keys[ibcratelimitmoduletypes.StoreKey],
// set the governance and delaymsg module accounts as the authority for conducting upgrades
[]string{
lib.GovModuleAddress.String(),
delaymsgmoduletypes.ModuleAddress.String(),
},
)

// TODO(CORE-834): Add IBCRatelimitKeeper to the IBC transfer stack.

// Create static IBC router, add transfer route, then set and seal it
ibcRouter := ibcporttypes.NewRouter()
ibcRouter.AddRoute(ibctransfertypes.ModuleName, transferIBCModule)
Expand Down Expand Up @@ -972,6 +989,7 @@ func New(
stakingtypes.ModuleName,
ibcexported.ModuleName,
ibctransfertypes.ModuleName,
ibcratelimitmoduletypes.ModuleName,
authtypes.ModuleName,
banktypes.ModuleName,
govtypes.ModuleName,
Expand Down Expand Up @@ -1014,6 +1032,7 @@ func New(
upgradetypes.ModuleName,
ibcexported.ModuleName,
ibctransfertypes.ModuleName,
ibcratelimitmoduletypes.ModuleName,
consensusparamtypes.ModuleName,
pricesmoduletypes.ModuleName,
assetsmoduletypes.ModuleName,
Expand Down Expand Up @@ -1052,6 +1071,7 @@ func New(
paramstypes.ModuleName,
upgradetypes.ModuleName,
ibctransfertypes.ModuleName,
ibcratelimitmoduletypes.ModuleName,
feegrant.ModuleName,
consensusparamtypes.ModuleName,
pricesmoduletypes.ModuleName,
Expand Down Expand Up @@ -1087,6 +1107,7 @@ func New(
paramstypes.ModuleName,
upgradetypes.ModuleName,
ibctransfertypes.ModuleName,
ibcratelimitmoduletypes.ModuleName,
feegrant.ModuleName,
consensusparamtypes.ModuleName,
pricesmoduletypes.ModuleName,
Expand Down
207 changes: 207 additions & 0 deletions protocol/x/ibcratelimit/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,26 @@ package keeper

import (
"fmt"
"math/big"

errorsmod "cosmossdk.io/errors"
sdklog "cosmossdk.io/log"
"github.com/cometbft/cometbft/libs/log"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/store/prefix"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/dydxprotocol/v4-chain/protocol/dtypes"
"github.com/dydxprotocol/v4-chain/protocol/lib"
"github.com/dydxprotocol/v4-chain/protocol/x/ibcratelimit/types"
)

var (
// TempTVLPlacerholder is a placeholder value for TVL.
// TODO(CORE-836): Remove this after `GetBaseline` is fully implemented.
TempTVLPlacerholder = big.NewInt(20_000_000_000_000)
)

type (
Keeper struct {
cdc codec.BinaryCodec
Expand All @@ -36,6 +46,203 @@ func NewKeeper(
}
}

// ProcessWithdrawal processes an outbound IBC transfer,
// by updating the capacity lists for the denom.
func (k Keeper) ProcessWithdrawal(
ctx sdk.Context,
denom string,
amount *big.Int,
) error {
denomCapacity := k.GetDenomCapacity(ctx, denom)

newCapacityList := make([]dtypes.SerializableInt,
0,
len(denomCapacity.CapacityList),
)

for i, capacity := range denomCapacity.CapacityList {
// Check that the withdrawal amount does not exceed each capacity.
if capacity.BigInt().Cmp(amount) < 0 {
return errorsmod.Wrapf(
types.ErrWithdrawalExceedsCapacity,
"denom = %v, capacity(index: %v) = %v, amount = %v",
denom,
i,
capacity.BigInt(),
amount,
)
}

// Debit each capacity in the list by the amount of withdrawal.
newCapacityList[i] = dtypes.NewIntFromBigInt(
new(big.Int).Sub(
capacity.BigInt(),
amount,
),
)
}

k.SetDenomCapacity(ctx, types.DenomCapacity{
Denom: denom,
CapacityList: newCapacityList,
})

return nil
}

// ProcessDeposit processes a inbound IBC transfer,
// by updating the capacity lists for the denom.
func (k Keeper) ProcessDeposit(
ctx sdk.Context,
denom string,
amount *big.Int,
) {
denomCapacity := k.GetDenomCapacity(ctx, denom)

newCapacityList := make([]dtypes.SerializableInt,
0,
len(denomCapacity.CapacityList),
)

// Credit each capacity in the list by the amount of deposit.
for i, capacity := range denomCapacity.CapacityList {
newCapacityList[i] = dtypes.NewIntFromBigInt(
new(big.Int).Add(
capacity.BigInt(),
amount,
),
)
}

k.SetDenomCapacity(ctx, types.DenomCapacity{
Denom: denom,
CapacityList: newCapacityList,
})
}

// GetBaseline returns the current capacity baseline for the given limiter.
// `baseline` formula:
//
// baseline = max(baseline_minimum, baseline_tvl_ppm * current_tvl)
func (k Keeper) GetBaseline(
ctx sdk.Context,
denom string,
limiter types.Limiter,
) *big.Int {
// Get the current TVL.
// TODO(CORE-836): Query bank Keeper to get current supply of the token.
currentTVL := TempTVLPlacerholder

return lib.BigMax(
limiter.BaselineMinimum.BigInt(),
lib.BigIntMulPpm(
currentTVL,
limiter.BaselineTvlPpm,
),
)
}

// SetLimitParams sets `LimitParams` for the given denom.
// Also overwrites the existing `DenomCapacity` object for the denom with a default `capacity_list` of the
// same length as the `limiters` list. Each `capacity` is initialized to the current baseline.
func (k Keeper) SetLimitParams(
ctx sdk.Context,
limitParams types.LimitParams,
) {
limitParamsStore := prefix.NewStore(ctx.KVStore(k.storeKey), []byte(types.LimitParamsKeyPrefix))
denomCapacityStore := prefix.NewStore(ctx.KVStore(k.storeKey), []byte(types.DenomCapacityKeyPrefix))

denomKey := []byte(limitParams.Denom)
// If the length of input limit params is zero, then remove both the
// limit params and the denom capacity in state.
if len(limitParams.Limiters) == 0 {
if limitParamsStore.Has(denomKey) {
limitParamsStore.Delete(denomKey)
}
if denomCapacityStore.Has(denomKey) {
denomCapacityStore.Delete(denomKey)
}
return
}

// Initialize the capacity list with the current baseline.
newCapacityList := make([]dtypes.SerializableInt, len(limitParams.Limiters))
for i, limiter := range limitParams.Limiters {
newCapacityList[i] = dtypes.NewIntFromBigInt(
k.GetBaseline(ctx, limitParams.Denom, limiter),
)
}
// Set correspondong `DenomCapacity` in state.
k.SetDenomCapacity(ctx, types.DenomCapacity{
Denom: limitParams.Denom,
CapacityList: newCapacityList,
})

b := k.cdc.MustMarshal(&limitParams)
limitParamsStore.Set(denomKey, b)
}

// GetLimitParams returns `LimitParams` for the given denom.
func (k Keeper) GetLimitParams(
ctx sdk.Context,
denom string,
) (val types.LimitParams) {
// Check state for the LimitParams.
store := prefix.NewStore(ctx.KVStore(k.storeKey), []byte(types.LimitParamsKeyPrefix))
b := store.Get([]byte(denom))

// If LimitParams does not exist in state, return a default value.
if b == nil {
return types.LimitParams{
Denom: denom,
}
}

// If LimitParams does exist in state, unmarshall and return the value.
k.cdc.MustUnmarshal(b, &val)
return val
}

// SetDenomCapacity sets `DenomCapacity` for the given denom.
func (k Keeper) SetDenomCapacity(
ctx sdk.Context,
denomCapacity types.DenomCapacity,
) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), []byte(types.DenomCapacityKeyPrefix))

key := []byte(denomCapacity.Denom)
// If there's no capacity entry to set, delete the key.
if len(denomCapacity.CapacityList) == 0 {
if store.Has(key) {
store.Delete(key)
}
} else {
b := k.cdc.MustMarshal(&denomCapacity)
store.Set(key, b)
}
}

// GetDenomCapacity returns `DenomCapacity` for the given denom.
func (k Keeper) GetDenomCapacity(
ctx sdk.Context,
denom string,
) (val types.DenomCapacity) {
// Check state for the DenomCapacity.
store := prefix.NewStore(ctx.KVStore(k.storeKey), []byte(types.DenomCapacityKeyPrefix))
b := store.Get([]byte(denom))

// If DenomCapacity does not exist in state, return a default value.
if b == nil {
return types.DenomCapacity{
Denom: denom,
}
}

// If DenomCapacity does exist in state, unmarshall and return the value.
k.cdc.MustUnmarshal(b, &val)
return val
}

func (k Keeper) HasAuthority(authority string) bool {
_, ok := k.authorities[authority]
return ok
Expand Down
Loading
Loading