Skip to content

Commit

Permalink
[CLOB-1050] update daemon liquidation info to include negative tnc su… (
Browse files Browse the repository at this point in the history
#879)

* [CLOB-1050] update daemon liquidation info to include negative tnc subaccounts and open positions

* fix proto format

* comments
  • Loading branch information
jayy04 authored Dec 12, 2023
1 parent 0a01da5 commit 95c3aeb
Show file tree
Hide file tree
Showing 14 changed files with 778 additions and 165 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,28 @@ export interface SubaccountLiquidationInfoSDKType {

quantums_insurance_lost: Long;
}
/**
* SubaccountOpenPositionInfo holds information about open positions for a
* perpetual.
*/

export interface SubaccountOpenPositionInfo {
/** The id of the perpetual. */
perpetualId: number;
subaccountsWithLongPosition: SubaccountId[];
subaccountsWithShortPosition: SubaccountId[];
}
/**
* SubaccountOpenPositionInfo holds information about open positions for a
* perpetual.
*/

export interface SubaccountOpenPositionInfoSDKType {
/** The id of the perpetual. */
perpetual_id: number;
subaccounts_with_long_position: SubaccountIdSDKType[];
subaccounts_with_short_position: SubaccountIdSDKType[];
}

function createBasePerpetualLiquidationInfo(): PerpetualLiquidationInfo {
return {
Expand Down Expand Up @@ -215,4 +237,69 @@ export const SubaccountLiquidationInfo = {
return message;
}

};

function createBaseSubaccountOpenPositionInfo(): SubaccountOpenPositionInfo {
return {
perpetualId: 0,
subaccountsWithLongPosition: [],
subaccountsWithShortPosition: []
};
}

export const SubaccountOpenPositionInfo = {
encode(message: SubaccountOpenPositionInfo, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
if (message.perpetualId !== 0) {
writer.uint32(8).uint32(message.perpetualId);
}

for (const v of message.subaccountsWithLongPosition) {
SubaccountId.encode(v!, writer.uint32(18).fork()).ldelim();
}

for (const v of message.subaccountsWithShortPosition) {
SubaccountId.encode(v!, writer.uint32(26).fork()).ldelim();
}

return writer;
},

decode(input: _m0.Reader | Uint8Array, length?: number): SubaccountOpenPositionInfo {
const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input);
let end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseSubaccountOpenPositionInfo();

while (reader.pos < end) {
const tag = reader.uint32();

switch (tag >>> 3) {
case 1:
message.perpetualId = reader.uint32();
break;

case 2:
message.subaccountsWithLongPosition.push(SubaccountId.decode(reader, reader.uint32()));
break;

case 3:
message.subaccountsWithShortPosition.push(SubaccountId.decode(reader, reader.uint32()));
break;

default:
reader.skipType(tag & 7);
break;
}
}

return message;
},

fromPartial(object: DeepPartial<SubaccountOpenPositionInfo>): SubaccountOpenPositionInfo {
const message = createBaseSubaccountOpenPositionInfo();
message.perpetualId = object.perpetualId ?? 0;
message.subaccountsWithLongPosition = object.subaccountsWithLongPosition?.map(e => SubaccountId.fromPartial(e)) || [];
message.subaccountsWithShortPosition = object.subaccountsWithShortPosition?.map(e => SubaccountId.fromPartial(e)) || [];
return message;
}

};
13 changes: 13 additions & 0 deletions proto/dydxprotocol/clob/liquidations.proto
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,16 @@ message SubaccountLiquidationInfo {
// covering this subaccount.
uint64 quantums_insurance_lost = 3;
}

// SubaccountOpenPositionInfo holds information about open positions for a
// perpetual.
message SubaccountOpenPositionInfo {
// The id of the perpetual.
uint32 perpetual_id = 1;
// The ids of the subaccounts with long positions.
repeated dydxprotocol.subaccounts.SubaccountId
subaccounts_with_long_position = 2 [ (gogoproto.nullable) = false ];
// The ids of the subaccounts with short positions.
repeated dydxprotocol.subaccounts.SubaccountId
subaccounts_with_short_position = 3 [ (gogoproto.nullable) = false ];
}
6 changes: 3 additions & 3 deletions protocol/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -584,8 +584,8 @@ func New(
// potentially liquidatable subaccounts and then encode them into an in-memory slice shared by
// the liquidations module.
// The in-memory data structure is shared by the x/clob module and liquidations daemon.
liquidatableSubaccountIds := liquidationtypes.NewLiquidatableSubaccountIds()
app.Server.WithLiquidatableSubaccountIds(liquidatableSubaccountIds)
daemonLiquidationInfo := liquidationtypes.NewDaemonLiquidationInfo()
app.Server.WithDaemonLiquidationInfo(daemonLiquidationInfo)

// Setup server for bridge messages.
// The in-memory data structure is shared by the x/bridge module and bridge daemon.
Expand Down Expand Up @@ -870,7 +870,7 @@ func New(
app.AccountKeeper,
app.BankKeeper,
app.SubaccountsKeeper,
liquidatableSubaccountIds,
daemonLiquidationInfo,
)
app.PerpetualsKeeper.SetClobKeeper(app.ClobKeeper)

Expand Down
13 changes: 7 additions & 6 deletions protocol/daemons/server/liquidation.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package server

import (
"context"

"github.com/cosmos/cosmos-sdk/telemetry"
"github.com/dydxprotocol/v4-chain/protocol/daemons/liquidation/api"
"github.com/dydxprotocol/v4-chain/protocol/daemons/server/types"
Expand All @@ -11,16 +12,16 @@ import (

// LiquidationServer defines the fields required for liquidation updates.
type LiquidationServer struct {
liquidatableSubaccountIds *liquidationtypes.LiquidatableSubaccountIds
daemonLiquidationInfo *liquidationtypes.DaemonLiquidationInfo
}

// WithLiquidatableSubaccountIds sets the `liquidatableSubaccountIds` field.
// WithDaemonLiquidationInfo sets the `daemonLiquidationInfo` field.
// This is updated by the liquidation service with a list of potentially liquidatable
// subaccount ids to be processed by the `PerpetualLiquidationsKeeper`.
func (server *Server) WithLiquidatableSubaccountIds(
liquidatableSubaccountIds *liquidationtypes.LiquidatableSubaccountIds,
func (server *Server) WithDaemonLiquidationInfo(
daemonLiquidationInfo *liquidationtypes.DaemonLiquidationInfo,
) *Server {
server.liquidatableSubaccountIds = liquidatableSubaccountIds
server.daemonLiquidationInfo = daemonLiquidationInfo
return server
}

Expand All @@ -41,7 +42,7 @@ func (s *Server) LiquidateSubaccounts(
metrics.Count,
)

s.liquidatableSubaccountIds.UpdateSubaccountIds(req.SubaccountIds)
s.daemonLiquidationInfo.UpdateLiquidatableSubaccountIds(req.SubaccountIds)

// Capture valid responses in metrics.
s.reportValidResponse(types.LiquidationsDaemonServiceName)
Expand Down
16 changes: 8 additions & 8 deletions protocol/daemons/server/liquidation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,33 +15,33 @@ import (
func TestLiquidateSubaccounts_Empty_Update(t *testing.T) {
mockGrpcServer := &mocks.GrpcServer{}
mockFileHandler := &mocks.FileHandler{}
liquidatableSubaccountIds := liquidationtypes.NewLiquidatableSubaccountIds()
daemonLiquidationInfo := liquidationtypes.NewDaemonLiquidationInfo()

s := createServerWithMocks(
t,
mockGrpcServer,
mockFileHandler,
).WithLiquidatableSubaccountIds(
liquidatableSubaccountIds,
).WithDaemonLiquidationInfo(
daemonLiquidationInfo,
)
_, err := s.LiquidateSubaccounts(grpc.Ctx, &api.LiquidateSubaccountsRequest{
SubaccountIds: []satypes.SubaccountId{},
})
require.NoError(t, err)
require.Empty(t, liquidatableSubaccountIds.GetSubaccountIds())
require.Empty(t, daemonLiquidationInfo.GetLiquidatableSubaccountIds())
}

func TestLiquidateSubaccounts_Multiple_Subaccount_Ids(t *testing.T) {
mockGrpcServer := &mocks.GrpcServer{}
mockFileHandler := &mocks.FileHandler{}
liquidatableSubaccountIds := liquidationtypes.NewLiquidatableSubaccountIds()
daemonLiquidationInfo := liquidationtypes.NewDaemonLiquidationInfo()

s := createServerWithMocks(
t,
mockGrpcServer,
mockFileHandler,
).WithLiquidatableSubaccountIds(
liquidatableSubaccountIds,
).WithDaemonLiquidationInfo(
daemonLiquidationInfo,
)

expectedSubaccountIds := []satypes.SubaccountId{
Expand All @@ -54,6 +54,6 @@ func TestLiquidateSubaccounts_Multiple_Subaccount_Ids(t *testing.T) {
})
require.NoError(t, err)

actualSubaccountIds := liquidatableSubaccountIds.GetSubaccountIds()
actualSubaccountIds := daemonLiquidationInfo.GetLiquidatableSubaccountIds()
require.Equal(t, expectedSubaccountIds, actualSubaccountIds)
}
117 changes: 117 additions & 0 deletions protocol/daemons/server/types/liquidations/daemon_liquidation_info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package types

import (
"sync"

clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types"
satypes "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types"
)

// DaemonLiquidationInfo maintains the list of subaccount ids to be liquidated
// in the next block. Methods are goroutine safe.
type DaemonLiquidationInfo struct {
sync.Mutex // lock
blockHeight uint32 // block height of the last update
liquidatableSubaccountIds []satypes.SubaccountId // liquidatable subaccount ids
negativeTncSubaccountIds []satypes.SubaccountId // negative total net collateral subaccount ids
subaccountsWithPositions map[uint32]*clobtypes.SubaccountOpenPositionInfo
}

// NewDaemonLiquidationInfo creates a new `NewDaemonLiquidationInfo` struct.
func NewDaemonLiquidationInfo() *DaemonLiquidationInfo {
return &DaemonLiquidationInfo{
liquidatableSubaccountIds: make([]satypes.SubaccountId, 0),
negativeTncSubaccountIds: make([]satypes.SubaccountId, 0),
subaccountsWithPositions: make(map[uint32]*clobtypes.SubaccountOpenPositionInfo),
}
}

// UpdateBlockHeight updates the struct with the given block height.
func (ls *DaemonLiquidationInfo) UpdateBlockHeight(blockHeight uint32) {
ls.Lock()
defer ls.Unlock()
ls.blockHeight = blockHeight
}

// GetBlockHeight returns the block height of the last update.
func (ls *DaemonLiquidationInfo) GetBlockHeight() uint32 {
ls.Lock()
defer ls.Unlock()
return ls.blockHeight
}

// UpdateLiquidatableSubaccountIds updates the struct with the given a list of potentially
// liquidatable subaccount ids.
func (ls *DaemonLiquidationInfo) UpdateLiquidatableSubaccountIds(updates []satypes.SubaccountId) {
ls.Lock()
defer ls.Unlock()
ls.liquidatableSubaccountIds = make([]satypes.SubaccountId, len(updates))
copy(ls.liquidatableSubaccountIds, updates)
}

// GetLiquidatableSubaccountIds returns the list of potentially liquidatable subaccount ids
// reported by the liquidation daemon.
func (ls *DaemonLiquidationInfo) GetLiquidatableSubaccountIds() []satypes.SubaccountId {
ls.Lock()
defer ls.Unlock()
results := make([]satypes.SubaccountId, len(ls.liquidatableSubaccountIds))
copy(results, ls.liquidatableSubaccountIds)
return results
}

// UpdateNegativeTncSubaccountIds updates the struct with the given a list of subaccount ids
// with negative total net collateral.
func (ls *DaemonLiquidationInfo) UpdateNegativeTncSubaccountIds(updates []satypes.SubaccountId) {
ls.Lock()
defer ls.Unlock()
ls.negativeTncSubaccountIds = make([]satypes.SubaccountId, len(updates))
copy(ls.negativeTncSubaccountIds, updates)
}

// GetNegativeTncSubaccountIds returns the list of subaccount ids with negative total net collateral
// reported by the liquidation daemon.
func (ls *DaemonLiquidationInfo) GetNegativeTncSubaccountIds() []satypes.SubaccountId {
ls.Lock()
defer ls.Unlock()
results := make([]satypes.SubaccountId, len(ls.negativeTncSubaccountIds))
copy(results, ls.negativeTncSubaccountIds)
return results
}

// UpdateSubaccountsWithPositions updates the struct with the given a list of subaccount ids with open positions.
func (ls *DaemonLiquidationInfo) UpdateSubaccountsWithPositions(
subaccountsWithPositions map[uint32]*clobtypes.SubaccountOpenPositionInfo,
) {
ls.Lock()
defer ls.Unlock()
ls.subaccountsWithPositions = make(map[uint32]*clobtypes.SubaccountOpenPositionInfo)
for perpetualId, info := range subaccountsWithPositions {
clone := &clobtypes.SubaccountOpenPositionInfo{
PerpetualId: perpetualId,
SubaccountsWithLongPosition: make([]satypes.SubaccountId, len(info.SubaccountsWithLongPosition)),
SubaccountsWithShortPosition: make([]satypes.SubaccountId, len(info.SubaccountsWithShortPosition)),
}
copy(clone.SubaccountsWithLongPosition, info.SubaccountsWithLongPosition)
copy(clone.SubaccountsWithShortPosition, info.SubaccountsWithShortPosition)
ls.subaccountsWithPositions[perpetualId] = clone
}
}

// GetSubaccountsWithPositions returns the list of subaccount ids with open positions.
func (ls *DaemonLiquidationInfo) GetSubaccountsWithPositions() map[uint32]*clobtypes.SubaccountOpenPositionInfo {
ls.Lock()
defer ls.Unlock()

result := make(map[uint32]*clobtypes.SubaccountOpenPositionInfo)
for perpetualId, info := range ls.subaccountsWithPositions {
clone := &clobtypes.SubaccountOpenPositionInfo{
PerpetualId: perpetualId,
SubaccountsWithLongPosition: make([]satypes.SubaccountId, len(info.SubaccountsWithLongPosition)),
SubaccountsWithShortPosition: make([]satypes.SubaccountId, len(info.SubaccountsWithShortPosition)),
}
copy(clone.SubaccountsWithLongPosition, info.SubaccountsWithLongPosition)
copy(clone.SubaccountsWithShortPosition, info.SubaccountsWithShortPosition)
result[perpetualId] = clone
}
return result
}
Loading

0 comments on commit 95c3aeb

Please sign in to comment.