Skip to content

Commit

Permalink
remove sequential ID requirement on liquidity tiers (#279)
Browse files Browse the repository at this point in the history
  • Loading branch information
tqin7 authored Sep 18, 2023
1 parent 6289294 commit 9c5fcd6
Show file tree
Hide file tree
Showing 8 changed files with 234 additions and 61 deletions.
8 changes: 8 additions & 0 deletions protocol/testutil/constants/perpetuals.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,14 @@ var LiquidityTiers = []perptypes.LiquidityTier{
BasePositionNotional: 100_000_000_000,
ImpactNotional: 50_454_000_000,
},
{
Id: 101,
Name: "101",
InitialMarginPpm: 200_000,
MaintenanceFractionPpm: 500_000,
BasePositionNotional: 1_000_000,
ImpactNotional: 2_500_000_000,
},
}

// Perpetual genesis parameters.
Expand Down
17 changes: 9 additions & 8 deletions protocol/testutil/keeper/perpetuals.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ func CreateTestLiquidityTiers(t *testing.T, ctx sdk.Context, k *keeper.Keeper) {
for _, l := range constants.LiquidityTiers {
_, err := k.CreateLiquidityTier(
ctx,
l.Id,
l.Name,
l.InitialMarginPpm,
l.MaintenanceFractionPpm,
Expand Down Expand Up @@ -211,8 +212,8 @@ func CreateNPerpetuals(
n int,
) ([]types.Perpetual, error) {
items := make([]types.Perpetual, n)
numLiquidityTiers := keeper.GetNumLiquidityTiers(ctx)
require.Greater(t, numLiquidityTiers, uint32(0))
allLiquidityTiers := keeper.GetAllLiquidityTiers(ctx)
require.Greater(t, len(allLiquidityTiers), 0)

for i := range items {
CreateNMarkets(t, ctx, pricesKeeper, n)
Expand All @@ -228,12 +229,12 @@ func CreateNPerpetuals(

perpetual, err := keeper.CreatePerpetual(
ctx,
uint32(i), // Id
fmt.Sprintf("%v", i), // Ticker
uint32(i), // MarketId
int32(i), // AtomicResolution
defaultFundingPpm, // DefaultFundingPpm
uint32(i%int(numLiquidityTiers)), // LiquidityTier
uint32(i), // Id
fmt.Sprintf("%v", i), // Ticker
uint32(i), // MarketId
int32(i), // AtomicResolution
defaultFundingPpm, // DefaultFundingPpm
allLiquidityTiers[i%len(allLiquidityTiers)].Id, // LiquidityTier
)
if err != nil {
return items, err
Expand Down
69 changes: 69 additions & 0 deletions protocol/testutil/liquidity_tier/liquidity_tier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package liquidity_tier

import (
perptypes "github.com/dydxprotocol/v4-chain/protocol/x/perpetuals/types"
)

type LtModifierOption func(cp *perptypes.LiquidityTier)

func WithId(id uint32) LtModifierOption {
return func(lt *perptypes.LiquidityTier) {
lt.Id = id
}
}

func WithName(name string) LtModifierOption {
return func(lt *perptypes.LiquidityTier) {
lt.Name = name
}
}

func WithInitialMarginPpm(initialMarginPpm uint32) LtModifierOption {
return func(lt *perptypes.LiquidityTier) {
lt.InitialMarginPpm = initialMarginPpm
}
}

func WithMaitenanceMarginFraction(maitenanceMarginFraction uint32) LtModifierOption {
return func(lt *perptypes.LiquidityTier) {
lt.MaintenanceFractionPpm = maitenanceMarginFraction
}
}

func WithBasePositionNotional(basePositionNotional uint64) LtModifierOption {
return func(lt *perptypes.LiquidityTier) {
lt.BasePositionNotional = basePositionNotional
}
}

func WithImpactNotional(impactNotional uint64) LtModifierOption {
return func(lt *perptypes.LiquidityTier) {
lt.ImpactNotional = impactNotional
}
}

// GenerateLiquidityTier returns a `LiquidityTier` object set to default values.
// Passing in `LtModifierOption` methods alters the value of the `LiquidityTier` returned.
// It will start with the default, valid `LiquidityTier` value defined within the method
// and make the requested modifications before returning the object.
//
// Example usage:
// `GenerateLiquidityTier(WithId(7))`
// This will start with the default `LiquidityTier` object defined within the method and
// return the newly-created object after overriding the values of `Id` to 7.
func GenerateLiquidityTier(optionalModifications ...LtModifierOption) *perptypes.LiquidityTier {
lt := &perptypes.LiquidityTier{
Id: 0,
Name: "Large-Cap",
InitialMarginPpm: 1_000_000,
MaintenanceFractionPpm: 1_000_000,
BasePositionNotional: 1_000_000,
ImpactNotional: 500_000_000,
}

for _, opt := range optionalModifications {
opt(lt)
}

return lt
}
1 change: 1 addition & 0 deletions protocol/x/perpetuals/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState)
for _, elem := range genState.LiquidityTiers {
_, err := k.CreateLiquidityTier(
ctx,
elem.Id,
elem.Name,
elem.InitialMarginPpm,
elem.MaintenanceFractionPpm,
Expand Down
1 change: 0 additions & 1 deletion protocol/x/perpetuals/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,4 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger {
}

func (k Keeper) InitializeForGenesis(ctx sdk.Context) {
k.setNumLiquidityTiers(ctx, uint32(0))
}
104 changes: 57 additions & 47 deletions protocol/x/perpetuals/keeper/perpetual.go
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,6 @@ func (k Keeper) sampleAllPerpetuals(ctx sdk.Context) (
err error,
) {
allPerpetuals := k.GetAllPerpetuals(ctx)
allLiquidityTiers := k.GetAllLiquidityTiers(ctx)

// Calculate `maxAbsPremiumVotePpm` of each liquidity tier.
liquidityTierToMaxAbsPremiumVotePpm := k.getLiquidityTiertoMaxAbsPremiumVotePpm(ctx)
Expand All @@ -460,9 +459,17 @@ func (k Keeper) sampleAllPerpetuals(ctx sdk.Context) (
}

// Get impact notional corresponding to this perpetual market (panic if its liquidity tier doesn't exist).
liquidityTier := allLiquidityTiers[perp.Params.LiquidityTier]
liquidityTier, err := k.GetLiquidityTier(ctx, perp.Params.LiquidityTier)
if err != nil {
panic(err)
}
bigImpactNotionalQuoteQuantums := new(big.Int).SetUint64(liquidityTier.ImpactNotional)

// Get `maxAbsPremiumVotePpm` for this perpetual's liquidity tier (panic if not found).
maxAbsPremiumVotePpm, exists := liquidityTierToMaxAbsPremiumVotePpm[perp.Params.LiquidityTier]
if !exists {
panic(types.ErrLiquidityTierDoesNotExist)
}
premiumPpm, err := k.clobKeeper.GetPricePremiumForPerpetual(
ctx,
perp.Params.Id,
Expand All @@ -471,8 +478,7 @@ func (k Keeper) sampleAllPerpetuals(ctx sdk.Context) (
BaseAtomicResolution: perp.Params.AtomicResolution,
QuoteAtomicResolution: lib.QuoteCurrencyAtomicResolution,
ImpactNotionalQuoteQuantums: bigImpactNotionalQuoteQuantums,
// Get `maxAbsPremiumVotePpm` for this perpetual's liquidity tier (panic if index is invalid).
MaxAbsPremiumVotePpm: liquidityTierToMaxAbsPremiumVotePpm[perp.Params.LiquidityTier],
MaxAbsPremiumVotePpm: maxAbsPremiumVotePpm,
},
)
if err != nil {
Expand Down Expand Up @@ -1140,7 +1146,7 @@ func (k Keeper) validatePerpetual(
}

// Validate `liquidityTier` exists.
if perpetual.Params.LiquidityTier >= k.GetNumLiquidityTiers(ctx) {
if !k.HasLiquidityTier(ctx, perpetual.Params.LiquidityTier) {
return errorsmod.Wrap(types.ErrLiquidityTierDoesNotExist, lib.Uint32ToString(perpetual.Params.LiquidityTier))
}

Expand Down Expand Up @@ -1221,8 +1227,11 @@ func (k Keeper) PerformStatefulPremiumVotesValidation(
)
}

// Get `maxAbsPremiumVotePpm` for this perpetual's liquidity tier (panic if index is invalid).
maxAbsPremiumVotePpm := liquidityTierToMaxAbsPremiumVotePpm[perpetual.Params.LiquidityTier]
// Get `maxAbsPremiumVotePpm` for this perpetual's liquidity tier (panic if not found).
maxAbsPremiumVotePpm, exists := liquidityTierToMaxAbsPremiumVotePpm[perpetual.Params.LiquidityTier]
if !exists {
panic(types.ErrLiquidityTierDoesNotExist)
}
// Check premium vote value is within bounds.
bigAbsPremiumPpm := new(big.Int).SetUint64(uint64(
lib.AbsInt32(vote.PremiumPpm),
Expand All @@ -1243,10 +1252,20 @@ func (k Keeper) PerformStatefulPremiumVotesValidation(

/* === LIQUIDITY TIER FUNCTIONS === */

// HasLiquidityTier checks if a liquidity tier exists in the store.
func (k Keeper) HasLiquidityTier(
ctx sdk.Context,
id uint32,
) (found bool) {
ltStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.LiquidityTierKeyPrefix))
return ltStore.Has(types.LiquidityTierKey(id))
}

// `CreateLiquidityTier` creates a new liquidity tier in the store.
// Returns an error if any of its fields fails validation.
func (k Keeper) CreateLiquidityTier(
ctx sdk.Context,
id uint32,
name string,
initialMarginPpm uint32,
maintenanceFractionPpm uint32,
Expand All @@ -1256,11 +1275,16 @@ func (k Keeper) CreateLiquidityTier(
liquidityTier types.LiquidityTier,
err error,
) {
// Get id for a new liquidity tier.
nextId := k.GetNumLiquidityTiers(ctx)
// Check if liquidity tier exists.
if k.HasLiquidityTier(ctx, id) {
return types.LiquidityTier{}, errorsmod.Wrap(
types.ErrLiquidityTierAlreadyExists,
lib.Uint32ToString(id),
)
}

liquidityTier = types.LiquidityTier{
Id: nextId,
Id: id,
Name: name,
InitialMarginPpm: initialMarginPpm,
MaintenanceFractionPpm: maintenanceFractionPpm,
Expand All @@ -1275,15 +1299,13 @@ func (k Keeper) CreateLiquidityTier(

// Set liquidity tier in store.
k.setLiquidityTier(ctx, liquidityTier)
// Increase `numLiquidityTiers` by 1.
k.setNumLiquidityTiers(ctx, nextId+1)

k.GetIndexerEventManager().AddTxnEvent(
ctx,
indexerevents.SubtypeLiquidityTier,
indexer_manager.GetB64EncodedEventMessage(
indexerevents.NewLiquidityTierUpsertEvent(
nextId,
id,
name,
initialMarginPpm,
maintenanceFractionPpm,
Expand Down Expand Up @@ -1347,22 +1369,6 @@ func (k Keeper) ModifyLiquidityTier(
return liquidityTier, nil
}

// `GetNumLiquidityTiers` returns the number of liquidity tiers created (`numLiquidityTiers`).
func (k Keeper) GetNumLiquidityTiers(ctx sdk.Context) (
numLiquidityTiers uint32,
) {
return k.getUint32InStore(ctx, types.NumLiquidityTiersKey)
}

// `setNumLiquidityTiers` sets number of liquidity tiers in store.
func (k Keeper) setNumLiquidityTiers(
ctx sdk.Context,
num uint32,
) {
// Set `numLiquidityTiers`.
k.setUint32InStore(ctx, types.NumLiquidityTiersKey, num)
}

// `GetLiquidityTier` gets a liquidity tier given its id.
func (k Keeper) GetLiquidityTier(ctx sdk.Context, id uint32) (
liquidityTier types.LiquidityTier,
Expand All @@ -1380,22 +1386,23 @@ func (k Keeper) GetLiquidityTier(ctx sdk.Context, id uint32) (
}

// `GetAllLiquidityTiers` returns all liquidity tiers, sorted by id.
func (k Keeper) GetAllLiquidityTiers(ctx sdk.Context) (
liquidityTiers []types.LiquidityTier,
) {
num := k.GetNumLiquidityTiers(ctx)
liquidityTiers = make([]types.LiquidityTier, num)
func (k Keeper) GetAllLiquidityTiers(ctx sdk.Context) (list []types.LiquidityTier) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.LiquidityTierKeyPrefix))
iterator := sdk.KVStorePrefixIterator(store, []byte{})

for i := uint32(0); i < num; i++ {
liquidityTier, err := k.GetLiquidityTier(ctx, i)
if err != nil {
panic(err)
}
defer iterator.Close()

liquidityTiers[i] = liquidityTier
for ; iterator.Valid(); iterator.Next() {
var val types.LiquidityTier
k.cdc.MustUnmarshal(iterator.Value(), &val)
list = append(list, val)
}

return liquidityTiers
sort.Slice(list, func(i, j int) bool {
return list[i].Id < list[j].Id
})

return list
}

// `setLiquidityTier` sets a liquidity tier in store.
Expand Down Expand Up @@ -1464,15 +1471,18 @@ func (k Keeper) SetMinNumVotesPerSample(ctx sdk.Context, num uint32) error {
}

// `getLiquidityTiertoMaxAbsPremiumVotePpm` returns `maxAbsPremiumVotePpm` for each liquidity tier
// (used for clamping premium votes), sorted by increasing liquidity tier ID.
func (k Keeper) getLiquidityTiertoMaxAbsPremiumVotePpm(ctx sdk.Context) []*big.Int {
// (used for clamping premium votes) as a map whose key is liquidity tier ID.
func (k Keeper) getLiquidityTiertoMaxAbsPremiumVotePpm(
ctx sdk.Context,
) (ltToMaxAbsPremiumVotePpm map[uint32]*big.Int) {
premiumVoteClampFactorPpm := k.GetPremiumVoteClampFactorPpm(ctx)
allLiquidityTiers := k.GetAllLiquidityTiers(ctx)
var maxAbsPremiumVotePpms = make([]*big.Int, len(allLiquidityTiers))
for i, liquidityTier := range allLiquidityTiers {
maxAbsPremiumVotePpms[i] = liquidityTier.GetMaxAbsFundingClampPpm(premiumVoteClampFactorPpm)
ltToMaxAbsPremiumVotePpm = make(map[uint32]*big.Int)
for _, liquidityTier := range allLiquidityTiers {
ltToMaxAbsPremiumVotePpm[liquidityTier.Id] =
liquidityTier.GetMaxAbsFundingClampPpm(premiumVoteClampFactorPpm)
}
return maxAbsPremiumVotePpms
return ltToMaxAbsPremiumVotePpm
}

// IsPositionUpdatable returns whether position of a perptual is updatable.
Expand Down
Loading

0 comments on commit 9c5fcd6

Please sign in to comment.