From 22867853bc40561f2024ed33bb4bcd64b0e484ac Mon Sep 17 00:00:00 2001 From: Tian Date: Fri, 20 Sep 2024 12:23:54 -0400 Subject: [PATCH] exclude deactivated vaults from megavault equity (#2308) --- .../src/codegen/dydxprotocol/vault/vault.ts | 10 +++++-- proto/dydxprotocol/vault/vault.proto | 1 + ...msg_server_withdraw_from_megavault_test.go | 28 +++++++++++++++++++ protocol/x/vault/keeper/vault.go | 6 +++- protocol/x/vault/keeper/vault_test.go | 2 +- protocol/x/vault/keeper/withdraw.go | 9 ++++-- protocol/x/vault/types/vault.pb.go | 1 + 7 files changed, 51 insertions(+), 6 deletions(-) diff --git a/indexer/packages/v4-protos/src/codegen/dydxprotocol/vault/vault.ts b/indexer/packages/v4-protos/src/codegen/dydxprotocol/vault/vault.ts index c12ef40159..0992b3373a 100644 --- a/indexer/packages/v4-protos/src/codegen/dydxprotocol/vault/vault.ts +++ b/indexer/packages/v4-protos/src/codegen/dydxprotocol/vault/vault.ts @@ -55,7 +55,10 @@ export enum VaultStatus { /** VAULT_STATUS_UNSPECIFIED - Default value, invalid and unused. */ VAULT_STATUS_UNSPECIFIED = 0, - /** VAULT_STATUS_DEACTIVATED - Don’t place orders. Does not count toward global vault balances. */ + /** + * VAULT_STATUS_DEACTIVATED - Don’t place orders. Does not count toward global vault balances. + * A vault can only be set to this status if its equity is non-positive. + */ VAULT_STATUS_DEACTIVATED = 1, /** VAULT_STATUS_STAND_BY - Don’t place orders. Does count towards global vault balances. */ @@ -74,7 +77,10 @@ export enum VaultStatusSDKType { /** VAULT_STATUS_UNSPECIFIED - Default value, invalid and unused. */ VAULT_STATUS_UNSPECIFIED = 0, - /** VAULT_STATUS_DEACTIVATED - Don’t place orders. Does not count toward global vault balances. */ + /** + * VAULT_STATUS_DEACTIVATED - Don’t place orders. Does not count toward global vault balances. + * A vault can only be set to this status if its equity is non-positive. + */ VAULT_STATUS_DEACTIVATED = 1, /** VAULT_STATUS_STAND_BY - Don’t place orders. Does count towards global vault balances. */ diff --git a/proto/dydxprotocol/vault/vault.proto b/proto/dydxprotocol/vault/vault.proto index 76c30482fe..ac87e8c0e9 100644 --- a/proto/dydxprotocol/vault/vault.proto +++ b/proto/dydxprotocol/vault/vault.proto @@ -27,6 +27,7 @@ enum VaultStatus { VAULT_STATUS_UNSPECIFIED = 0; // Don’t place orders. Does not count toward global vault balances. + // A vault can only be set to this status if its equity is non-positive. VAULT_STATUS_DEACTIVATED = 1; // Don’t place orders. Does count towards global vault balances. diff --git a/protocol/x/vault/keeper/msg_server_withdraw_from_megavault_test.go b/protocol/x/vault/keeper/msg_server_withdraw_from_megavault_test.go index 6f9ae542ae..031655c0e6 100644 --- a/protocol/x/vault/keeper/msg_server_withdraw_from_megavault_test.go +++ b/protocol/x/vault/keeper/msg_server_withdraw_from_megavault_test.go @@ -185,6 +185,34 @@ func TestMsgWithdrawFromMegavault(t *testing.T) { expectedTotalShares: 500, // unchanged expectedOwnerShares: 47, // unchanged }, + "Success: Withdraw some unlocked shares (8% of total), one deactivated sub-vault is excluded": { + mainVaultBalance: big.NewInt(1_234), + totalShares: 500, + owner: constants.DaveAccAddress.String(), + ownerTotalShares: 47, + ownerLockedShares: 7, + vaults: []VaultSetup{ + { + id: constants.Vault_Clob0, + params: vaulttypes.VaultParams{ + Status: vaulttypes.VaultStatus_VAULT_STATUS_DEACTIVATED, + }, + assetQuoteQuantums: big.NewInt(-400), + positionBaseQuantums: big.NewInt(0), + clobPair: constants.ClobPair_Btc, + perpetual: constants.BtcUsd_20PercentInitial_10PercentMaintenance, + marketParam: constants.TestMarketParams[0], + marketPrice: constants.TestMarketPrices[0], + postWithdrawalEquity: big.NewInt(-400), // unchanged + }, + }, + sharesToWithdraw: 40, + minQuoteQuantums: 50, + deliverTxFails: false, + redeemedQuoteQuantums: 98, // 1234 * 0.08 = 98.72 ~= 98 (rounded down) + expectedTotalShares: 460, // 500 - 40 + expectedOwnerShares: 7, // 47 - 40 + }, "Success: Withdraw some unlocked shares (0.4444% of total), 888_888 quantums in main vault, " + "one quoting sub-vault with negative equity": { mainVaultBalance: big.NewInt(888_888), diff --git a/protocol/x/vault/keeper/vault.go b/protocol/x/vault/keeper/vault.go index e6952607ff..19e883f168 100644 --- a/protocol/x/vault/keeper/vault.go +++ b/protocol/x/vault/keeper/vault.go @@ -18,7 +18,7 @@ import ( // GetMegavaultEquity returns the equity of the megavault (in quote quantums), which consists of // - equity of the megavault main subaccount -// - equity of all vaults (if positive) +// - equity of all vaults (if not-deactivated and positive) func (k Keeper) GetMegavaultEquity(ctx sdk.Context) (*big.Int, error) { megavaultEquity, err := k.GetSubaccountEquity(ctx, types.MegavaultMainSubaccount) if err != nil { @@ -32,6 +32,10 @@ func (k Keeper) GetMegavaultEquity(ctx sdk.Context) (*big.Int, error) { var vaultParams types.VaultParams k.cdc.MustUnmarshal(vaultParamsIterator.Value(), &vaultParams) + if vaultParams.Status == types.VaultStatus_VAULT_STATUS_DEACTIVATED { + continue + } + vaultId, err := types.GetVaultIdFromStateKey(vaultParamsIterator.Key()) if err != nil { log.ErrorLogWithError(ctx, "Failed to get vault ID from state key", err) diff --git a/protocol/x/vault/keeper/vault_test.go b/protocol/x/vault/keeper/vault_test.go index 76078fab3f..aa47b8dbee 100644 --- a/protocol/x/vault/keeper/vault_test.go +++ b/protocol/x/vault/keeper/vault_test.go @@ -484,7 +484,7 @@ func TestGetMegavaultEquity(t *testing.T) { big.NewInt(345), big.NewInt(-5), }, - expectedMegavaultEquity: big.NewInt(1_345), + expectedMegavaultEquity: big.NewInt(1_345), // deactivated vault is not counted. }, } for name, tc := range tests { diff --git a/protocol/x/vault/keeper/withdraw.go b/protocol/x/vault/keeper/withdraw.go index a7e024cb61..957518e7c5 100644 --- a/protocol/x/vault/keeper/withdraw.go +++ b/protocol/x/vault/keeper/withdraw.go @@ -165,6 +165,13 @@ func (k Keeper) WithdrawFromMegavault( vaultParamsIterator := k.getVaultParamsIterator(ctx) defer vaultParamsIterator.Close() for ; vaultParamsIterator.Valid(); vaultParamsIterator.Next() { + var vaultParams types.VaultParams + k.cdc.MustUnmarshal(vaultParamsIterator.Value(), &vaultParams) + // Skip deactivated vaults. + if vaultParams.Status == types.VaultStatus_VAULT_STATUS_DEACTIVATED { + continue + } + vaultId, err := types.GetVaultIdFromStateKey(vaultParamsIterator.Key()) if err != nil { log.ErrorLogWithError( @@ -174,8 +181,6 @@ func (k Keeper) WithdrawFromMegavault( ) continue } - var vaultParams types.VaultParams - k.cdc.MustUnmarshal(vaultParamsIterator.Value(), &vaultParams) _, perpetual, marketParam, marketPrice, err := k.GetVaultClobPerpAndMarket(ctx, *vaultId) if err != nil { diff --git a/protocol/x/vault/types/vault.pb.go b/protocol/x/vault/types/vault.pb.go index 738c7ffc9c..10afe70933 100644 --- a/protocol/x/vault/types/vault.pb.go +++ b/protocol/x/vault/types/vault.pb.go @@ -57,6 +57,7 @@ const ( // Default value, invalid and unused. VaultStatus_VAULT_STATUS_UNSPECIFIED VaultStatus = 0 // Don’t place orders. Does not count toward global vault balances. + // A vault can only be set to this status if its equity is non-positive. VaultStatus_VAULT_STATUS_DEACTIVATED VaultStatus = 1 // Don’t place orders. Does count towards global vault balances. VaultStatus_VAULT_STATUS_STAND_BY VaultStatus = 2