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

[CLOB-1050] update daemon liquidation info to include negative tnc su… #879

Merged
merged 4 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
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)
}
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
Copy link
Contributor

Choose a reason for hiding this comment

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

Are all of the liquidatable / negative TNC subaccounts read from the same block height (which is the same as this blockHeight field)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes - I will be updating the liquidation daemon to make height based queries so everything is read from the same height

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
Loading