From aaa08601b7e664a8d68bc769922fa594002808d7 Mon Sep 17 00:00:00 2001 From: vincentwschau <99756290+vincentwschau@users.noreply.github.com> Date: Thu, 19 Sep 2024 12:54:11 -0400 Subject: [PATCH] [TRA-621] Sweep bank funds of megavault into subaccount. (#2293) --- protocol/app/app.go | 2 + protocol/mocks/AssetsKeeper.go | 201 ++++++++++++++++++ protocol/mocks/Makefile | 1 + protocol/testutil/keeper/vault.go | 2 + protocol/x/assets/types/types.go | 31 +++ protocol/x/vault/abci.go | 1 + protocol/x/vault/keeper/keeper.go | 6 + protocol/x/vault/keeper/sweep_funds.go | 48 +++++ protocol/x/vault/keeper/sweep_funds_test.go | 219 ++++++++++++++++++++ protocol/x/vault/types/expected_keepers.go | 27 +++ protocol/x/vault/types/megavault.go | 3 +- 11 files changed, 540 insertions(+), 1 deletion(-) create mode 100644 protocol/mocks/AssetsKeeper.go create mode 100644 protocol/x/vault/keeper/sweep_funds.go create mode 100644 protocol/x/vault/keeper/sweep_funds_test.go diff --git a/protocol/app/app.go b/protocol/app/app.go index d33bdfe888..7a5f350eee 100644 --- a/protocol/app/app.go +++ b/protocol/app/app.go @@ -1178,6 +1178,8 @@ func New( app.VaultKeeper = *vaultmodulekeeper.NewKeeper( appCodec, keys[vaultmoduletypes.StoreKey], + app.AssetsKeeper, + app.BankKeeper, app.ClobKeeper, app.DelayMsgKeeper, app.PerpetualsKeeper, diff --git a/protocol/mocks/AssetsKeeper.go b/protocol/mocks/AssetsKeeper.go new file mode 100644 index 0000000000..b117d6596b --- /dev/null +++ b/protocol/mocks/AssetsKeeper.go @@ -0,0 +1,201 @@ +// Code generated by mockery v2.44.1. DO NOT EDIT. + +package mocks + +import ( + big "math/big" + + assetstypes "github.com/dydxprotocol/v4-chain/protocol/x/assets/types" + + mock "github.com/stretchr/testify/mock" + + types "github.com/cosmos/cosmos-sdk/types" +) + +// AssetsKeeper is an autogenerated mock type for the AssetsKeeper type +type AssetsKeeper struct { + mock.Mock +} + +// ConvertAssetToCoin provides a mock function with given fields: ctx, assetId, quantums +func (_m *AssetsKeeper) ConvertAssetToCoin(ctx types.Context, assetId uint32, quantums *big.Int) (*big.Int, types.Coin, error) { + ret := _m.Called(ctx, assetId, quantums) + + if len(ret) == 0 { + panic("no return value specified for ConvertAssetToCoin") + } + + var r0 *big.Int + var r1 types.Coin + var r2 error + if rf, ok := ret.Get(0).(func(types.Context, uint32, *big.Int) (*big.Int, types.Coin, error)); ok { + return rf(ctx, assetId, quantums) + } + if rf, ok := ret.Get(0).(func(types.Context, uint32, *big.Int) *big.Int); ok { + r0 = rf(ctx, assetId, quantums) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } + } + + if rf, ok := ret.Get(1).(func(types.Context, uint32, *big.Int) types.Coin); ok { + r1 = rf(ctx, assetId, quantums) + } else { + r1 = ret.Get(1).(types.Coin) + } + + if rf, ok := ret.Get(2).(func(types.Context, uint32, *big.Int) error); ok { + r2 = rf(ctx, assetId, quantums) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// CreateAsset provides a mock function with given fields: ctx, assetId, symbol, denom, denomExponent, hasMarket, marketId, atomicResolution +func (_m *AssetsKeeper) CreateAsset(ctx types.Context, assetId uint32, symbol string, denom string, denomExponent int32, hasMarket bool, marketId uint32, atomicResolution int32) (assetstypes.Asset, error) { + ret := _m.Called(ctx, assetId, symbol, denom, denomExponent, hasMarket, marketId, atomicResolution) + + if len(ret) == 0 { + panic("no return value specified for CreateAsset") + } + + var r0 assetstypes.Asset + var r1 error + if rf, ok := ret.Get(0).(func(types.Context, uint32, string, string, int32, bool, uint32, int32) (assetstypes.Asset, error)); ok { + return rf(ctx, assetId, symbol, denom, denomExponent, hasMarket, marketId, atomicResolution) + } + if rf, ok := ret.Get(0).(func(types.Context, uint32, string, string, int32, bool, uint32, int32) assetstypes.Asset); ok { + r0 = rf(ctx, assetId, symbol, denom, denomExponent, hasMarket, marketId, atomicResolution) + } else { + r0 = ret.Get(0).(assetstypes.Asset) + } + + if rf, ok := ret.Get(1).(func(types.Context, uint32, string, string, int32, bool, uint32, int32) error); ok { + r1 = rf(ctx, assetId, symbol, denom, denomExponent, hasMarket, marketId, atomicResolution) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetAllAssets provides a mock function with given fields: ctx +func (_m *AssetsKeeper) GetAllAssets(ctx types.Context) []assetstypes.Asset { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetAllAssets") + } + + var r0 []assetstypes.Asset + if rf, ok := ret.Get(0).(func(types.Context) []assetstypes.Asset); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]assetstypes.Asset) + } + } + + return r0 +} + +// GetAsset provides a mock function with given fields: ctx, id +func (_m *AssetsKeeper) GetAsset(ctx types.Context, id uint32) (assetstypes.Asset, bool) { + ret := _m.Called(ctx, id) + + if len(ret) == 0 { + panic("no return value specified for GetAsset") + } + + var r0 assetstypes.Asset + var r1 bool + if rf, ok := ret.Get(0).(func(types.Context, uint32) (assetstypes.Asset, bool)); ok { + return rf(ctx, id) + } + if rf, ok := ret.Get(0).(func(types.Context, uint32) assetstypes.Asset); ok { + r0 = rf(ctx, id) + } else { + r0 = ret.Get(0).(assetstypes.Asset) + } + + if rf, ok := ret.Get(1).(func(types.Context, uint32) bool); ok { + r1 = rf(ctx, id) + } else { + r1 = ret.Get(1).(bool) + } + + return r0, r1 +} + +// IsPositionUpdatable provides a mock function with given fields: ctx, id +func (_m *AssetsKeeper) IsPositionUpdatable(ctx types.Context, id uint32) (bool, error) { + ret := _m.Called(ctx, id) + + if len(ret) == 0 { + panic("no return value specified for IsPositionUpdatable") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(types.Context, uint32) (bool, error)); ok { + return rf(ctx, id) + } + if rf, ok := ret.Get(0).(func(types.Context, uint32) bool); ok { + r0 = rf(ctx, id) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(types.Context, uint32) error); ok { + r1 = rf(ctx, id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ModifyAsset provides a mock function with given fields: ctx, id, hasMarket, marketId +func (_m *AssetsKeeper) ModifyAsset(ctx types.Context, id uint32, hasMarket bool, marketId uint32) (assetstypes.Asset, error) { + ret := _m.Called(ctx, id, hasMarket, marketId) + + if len(ret) == 0 { + panic("no return value specified for ModifyAsset") + } + + var r0 assetstypes.Asset + var r1 error + if rf, ok := ret.Get(0).(func(types.Context, uint32, bool, uint32) (assetstypes.Asset, error)); ok { + return rf(ctx, id, hasMarket, marketId) + } + if rf, ok := ret.Get(0).(func(types.Context, uint32, bool, uint32) assetstypes.Asset); ok { + r0 = rf(ctx, id, hasMarket, marketId) + } else { + r0 = ret.Get(0).(assetstypes.Asset) + } + + if rf, ok := ret.Get(1).(func(types.Context, uint32, bool, uint32) error); ok { + r1 = rf(ctx, id, hasMarket, marketId) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewAssetsKeeper creates a new instance of AssetsKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewAssetsKeeper(t interface { + mock.TestingT + Cleanup(func()) +}) *AssetsKeeper { + mock := &AssetsKeeper{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/protocol/mocks/Makefile b/protocol/mocks/Makefile index 5300d51f91..a5741c4712 100644 --- a/protocol/mocks/Makefile +++ b/protocol/mocks/Makefile @@ -63,3 +63,4 @@ mock-gen: @go run github.com/vektra/mockery/v2 --name=OracleClient --dir=$(GOPATH)/pkg/mod/github.com/skip-mev/slinky@$(SLINKY_VERSION)/service/clients/oracle --recursive --output=./mocks @go run github.com/vektra/mockery/v2 --name=ExtendVoteHandler --dir=$(GOPATH)/pkg/mod/github.com/dydxprotocol/cosmos-sdk@$(COSMOS_VERSION)/types --recursive --output=./mocks @go run github.com/vektra/mockery/v2 --name=UpdateMarketPriceTxDecoder --dir=./app/process --recursive --output=./mocks + @go run github.com/vektra/mockery/v2 --name=AssetsKeeper --dir=./x//types --recursive --output=./mocks diff --git a/protocol/testutil/keeper/vault.go b/protocol/testutil/keeper/vault.go index 3170f71306..e68ec78d11 100644 --- a/protocol/testutil/keeper/vault.go +++ b/protocol/testutil/keeper/vault.go @@ -62,6 +62,8 @@ func createVaultKeeper( k := keeper.NewKeeper( cdc, storeKey, + &mocks.AssetsKeeper{}, + &mocks.BankKeeper{}, &mocks.ClobKeeper{}, &mocks.DelayMsgKeeper{}, &mocks.PerpetualsKeeper{}, diff --git a/protocol/x/assets/types/types.go b/protocol/x/assets/types/types.go index ab1254f4c2..766427768a 100644 --- a/protocol/x/assets/types/types.go +++ b/protocol/x/assets/types/types.go @@ -1 +1,32 @@ package types + +import ( + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type AssetsKeeper interface { + ConvertAssetToCoin(ctx sdk.Context, assetId uint32, quantums *big.Int) (*big.Int, sdk.Coin, error) + CreateAsset( + ctx sdk.Context, + assetId uint32, + symbol string, + denom string, + denomExponent int32, + hasMarket bool, + marketId uint32, + atomicResolution int32, + ) ( + Asset, + error, + ) + + GetAsset(ctx sdk.Context, id uint32) (Asset, bool) + + GetAllAssets(ctx sdk.Context) []Asset + + IsPositionUpdatable(ctx sdk.Context, id uint32) (bool, error) + + ModifyAsset(ctx sdk.Context, id uint32, hasMarket bool, marketId uint32) (Asset, error) +} diff --git a/protocol/x/vault/abci.go b/protocol/x/vault/abci.go index dcbce5d759..2670321b2f 100644 --- a/protocol/x/vault/abci.go +++ b/protocol/x/vault/abci.go @@ -36,6 +36,7 @@ func EndBlocker( // halting the chain. if err := abci.RunCached(ctx, func(ctx sdk.Context) error { keeper.RefreshAllVaultOrders(ctx) + keeper.SweepMainVaultBankBalance(ctx) return nil }); err != nil { log.ErrorLog( diff --git a/protocol/x/vault/keeper/keeper.go b/protocol/x/vault/keeper/keeper.go index 35579dc4af..78ae6f6170 100644 --- a/protocol/x/vault/keeper/keeper.go +++ b/protocol/x/vault/keeper/keeper.go @@ -16,6 +16,8 @@ type ( Keeper struct { cdc codec.BinaryCodec storeKey storetypes.StoreKey + assetsKeeper types.AssetsKeeper + bankKeeper types.BankKeeper clobKeeper types.ClobKeeper delayMsgKeeper types.DelayMsgKeeper perpetualsKeeper types.PerpetualsKeeper @@ -30,6 +32,8 @@ type ( func NewKeeper( cdc codec.BinaryCodec, storeKey storetypes.StoreKey, + assetsKeeper types.AssetsKeeper, + bankKeeper types.BankKeeper, clobKeeper types.ClobKeeper, delayMsgKeeper types.DelayMsgKeeper, perpetualsKeeper types.PerpetualsKeeper, @@ -42,6 +46,8 @@ func NewKeeper( return &Keeper{ cdc: cdc, storeKey: storeKey, + assetsKeeper: assetsKeeper, + bankKeeper: bankKeeper, clobKeeper: clobKeeper, delayMsgKeeper: delayMsgKeeper, perpetualsKeeper: perpetualsKeeper, diff --git a/protocol/x/vault/keeper/sweep_funds.go b/protocol/x/vault/keeper/sweep_funds.go new file mode 100644 index 0000000000..93237fd23f --- /dev/null +++ b/protocol/x/vault/keeper/sweep_funds.go @@ -0,0 +1,48 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/dydxprotocol/v4-chain/protocol/lib/log" + assettypes "github.com/dydxprotocol/v4-chain/protocol/x/assets/types" + "github.com/dydxprotocol/v4-chain/protocol/x/vault/types" +) + +// SweepMainVaultBankBalances deposits any usdc balance from the Megavault main vault bank balance +// into the Megavault main vault subaccount balance. +func (k Keeper) SweepMainVaultBankBalance( + ctx sdk.Context, +) { + usdcAsset, exists := k.assetsKeeper.GetAsset(ctx, assettypes.AssetUsdc.Id) + if !exists { + log.ErrorLog( + ctx, + "SweepMainVaultBankBalance: Usdc asset not found in state", + ) + return + } + mainVaultBalance := k.bankKeeper.GetBalance( + ctx, + types.MegavaultMainAddress, + usdcAsset.Denom, + ) + // No funds to sweep + if mainVaultBalance.Amount.BigInt().Sign() <= 0 { + return + } + + err := k.subaccountsKeeper.DepositFundsFromAccountToSubaccount( + ctx, + types.MegavaultMainAddress, + types.MegavaultMainSubaccount, + usdcAsset.Id, + mainVaultBalance.Amount.BigInt(), + ) + if err != nil { + log.ErrorLogWithError( + ctx, + "SweepMainVaultBankBalance: Failed to sweep funds from main vault bank balance to subaccount", + err, + ) + return + } +} diff --git a/protocol/x/vault/keeper/sweep_funds_test.go b/protocol/x/vault/keeper/sweep_funds_test.go new file mode 100644 index 0000000000..5290426a87 --- /dev/null +++ b/protocol/x/vault/keeper/sweep_funds_test.go @@ -0,0 +1,219 @@ +package keeper_test + +import ( + "math/big" + "testing" + + "github.com/cosmos/gogoproto/proto" + + sdkmath "cosmossdk.io/math" + "github.com/cometbft/cometbft/types" + sdktypes "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + testapp "github.com/dydxprotocol/v4-chain/protocol/testutil/app" + "github.com/dydxprotocol/v4-chain/protocol/testutil/constants" + testutil "github.com/dydxprotocol/v4-chain/protocol/testutil/util" + assettypes "github.com/dydxprotocol/v4-chain/protocol/x/assets/types" + sendingtypes "github.com/dydxprotocol/v4-chain/protocol/x/sending/types" + satypes "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types" + vaulttypes "github.com/dydxprotocol/v4-chain/protocol/x/vault/types" + "github.com/stretchr/testify/require" +) + +func TestSweepMainVaultBankBalance(t *testing.T) { + tests := map[string]struct { + // Bank balance of main vault + bankBalance int64 + // Subaccount balance of main vault + subaccountBalance *big.Int + // Expected bank balance of main vault + expectedBankBalance int64 + // Expected subaccount balance of main vault + expectedSubaccountBalance *big.Int + }{ + "Zero bank balance, zero subaccount balance": { + bankBalance: 0, + subaccountBalance: big.NewInt(0), + expectedBankBalance: 0, + expectedSubaccountBalance: big.NewInt(0), + }, + "100_000_000 quantums bank balance, zero subaccount balance": { + bankBalance: 100_000_000, + subaccountBalance: big.NewInt(0), + expectedBankBalance: 0, + expectedSubaccountBalance: big.NewInt(100_000_000), + }, + "100_000_000 quantums bank balance, 50_000_000 subaccount balance": { + bankBalance: 100_000_000, + subaccountBalance: big.NewInt(50_000_000), + expectedBankBalance: 0, + expectedSubaccountBalance: big.NewInt(150_000_000), + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + // Initialize vaults with their equities. + tApp := testapp.NewTestAppBuilder(t).WithGenesisDocFn(func() (genesis types.GenesisDoc) { + genesis = testapp.DefaultGenesis() + testapp.UpdateGenesisDocWithAppStateForModule( + &genesis, + func(genesisState *satypes.GenesisState) { + genesisState.Subaccounts = []satypes.Subaccount{ + { + Id: &vaulttypes.MegavaultMainSubaccount, + AssetPositions: []*satypes.AssetPosition{ + testutil.CreateSingleAssetPosition( + assettypes.AssetUsdc.Id, + tc.subaccountBalance, + ), + }, + }, + } + }, + ) + testapp.UpdateGenesisDocWithAppStateForModule( + &genesis, + func(genesisState *banktypes.GenesisState) { + genesisState.Balances = append(genesisState.Balances, banktypes.Balance{ + Address: vaulttypes.MegavaultMainAddress.String(), + Coins: sdktypes.Coins{ + sdktypes.NewCoin(constants.Usdc.Denom, sdkmath.NewInt(tc.bankBalance)), + }, + }) + }, + ) + return genesis + }).Build() + ctx := tApp.InitChain() + k := tApp.App.VaultKeeper + + k.SweepMainVaultBankBalance(ctx) + + mainVaultSubaccount := tApp.App.SubaccountsKeeper.GetSubaccount(ctx, vaulttypes.MegavaultMainSubaccount) + require.Equal(t, tc.expectedSubaccountBalance, mainVaultSubaccount.AssetPositions[0].Quantums.BigInt()) + mainVaultBankBalance := tApp.App.BankKeeper.GetBalance( + ctx, + vaulttypes.MegavaultMainAddress, + constants.Usdc.Denom, + ).Amount + require.Equal(t, sdkmath.NewIntFromBigInt(big.NewInt(tc.expectedBankBalance)), mainVaultBankBalance) + }) + } +} + +func TestSweepMainVaultBankBalance_EndBlock(t *testing.T) { + tests := map[string]struct { + // Bank balance of main vault + bankBalance int64 + // Subaccount balance of main vault + subaccountBalance uint64 + // Expected bank balance of main vault + expectedBankBalance int64 + // Expected subaccount balance of main vault + expectedSubaccountBalance *big.Int + }{ + "Zero bank balance, zero subaccount balance": { + bankBalance: 0, + subaccountBalance: 0, + expectedBankBalance: 0, + expectedSubaccountBalance: big.NewInt(0), + }, + "100_000_000 quantums bank balance, zero subaccount balance": { + bankBalance: 100_000_000, + subaccountBalance: 0, + expectedBankBalance: 0, + expectedSubaccountBalance: big.NewInt(100_000_000), + }, + "100_000_000 quantums bank balance, 50_000_000 subaccount balance": { + bankBalance: 100_000_000, + subaccountBalance: 50_000_000, + expectedBankBalance: 0, + expectedSubaccountBalance: big.NewInt(150_000_000), + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + tApp := testapp.NewTestAppBuilder(t).WithGenesisDocFn(func() (genesis types.GenesisDoc) { + genesis = testapp.DefaultGenesis() + testapp.UpdateGenesisDocWithAppStateForModule( + &genesis, + func(genesisState *satypes.GenesisState) { + genesisState.Subaccounts = []satypes.Subaccount{ + { + Id: &vaulttypes.MegavaultMainSubaccount, + AssetPositions: []*satypes.AssetPosition{ + testutil.CreateSingleAssetPosition( + assettypes.AssetUsdc.Id, + big.NewInt(0), + ), + }, + }, + } + }, + ) + return genesis + }).Build() + + // Fund the subaccount and bank balance of megavault + ctx := tApp.AdvanceToBlock(2, testapp.AdvanceToBlockOptions{}) + if tc.subaccountBalance > 0 { + var msg proto.Message + depositMsg := sendingtypes.MsgDepositToSubaccount{ + Sender: constants.AliceAccAddress.String(), + Recipient: vaulttypes.MegavaultMainSubaccount, + AssetId: constants.Usdc.Id, + Quantums: tc.subaccountBalance, + } + msg = &depositMsg + for _, checkTx := range testapp.MustMakeCheckTxsWithSdkMsg( + ctx, + tApp.App, + testapp.MustMakeCheckTxOptions{ + AccAddressForSigning: constants.AliceAccAddress.String(), + Gas: 1000000, + FeeAmt: constants.TestFeeCoins_5Cents, + }, + msg, + ) { + resp := tApp.CheckTx(checkTx) + require.Condition(t, resp.IsOK, "Expected CheckTx to succeed. Response: %+v", resp) + } + } + if tc.bankBalance > 0 { + var msg proto.Message + bankSendMsg := banktypes.MsgSend{ + FromAddress: constants.AliceAccAddress.String(), + ToAddress: vaulttypes.MegavaultMainAddress.String(), + Amount: sdktypes.Coins{ + sdktypes.NewCoin(constants.Usdc.Denom, sdkmath.NewInt(tc.bankBalance)), + }, + } + msg = &bankSendMsg + for _, checkTx := range testapp.MustMakeCheckTxsWithSdkMsg( + ctx, + tApp.App, + testapp.MustMakeCheckTxOptions{ + AccAddressForSigning: constants.AliceAccAddress.String(), + Gas: 1000000, + FeeAmt: constants.TestFeeCoins_5Cents, + }, + msg, + ) { + resp := tApp.CheckTx(checkTx) + require.Condition(t, resp.IsOK, "Expected CheckTx to succeed. Response: %+v", resp) + } + } + // Advance block to execute EndBlocker + ctx = tApp.AdvanceToBlock(3, testapp.AdvanceToBlockOptions{}) + + mainVaultSubaccount := tApp.App.SubaccountsKeeper.GetSubaccount(ctx, vaulttypes.MegavaultMainSubaccount) + require.Equal(t, tc.expectedSubaccountBalance, mainVaultSubaccount.AssetPositions[0].Quantums.BigInt()) + mainVaultBankBalance := tApp.App.BankKeeper.GetBalance( + ctx, + vaulttypes.MegavaultMainAddress, + constants.Usdc.Denom, + ).Amount + require.Equal(t, sdkmath.NewIntFromBigInt(big.NewInt(tc.expectedBankBalance)), mainVaultBankBalance) + }) + } +} diff --git a/protocol/x/vault/types/expected_keepers.go b/protocol/x/vault/types/expected_keepers.go index 398e2bc71f..643d8b012e 100644 --- a/protocol/x/vault/types/expected_keepers.go +++ b/protocol/x/vault/types/expected_keepers.go @@ -1,10 +1,12 @@ package types import ( + "context" "math/big" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/dydxprotocol/v4-chain/protocol/lib/margin" + assettypes "github.com/dydxprotocol/v4-chain/protocol/x/assets/types" clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" perptypes "github.com/dydxprotocol/v4-chain/protocol/x/perpetuals/types" pricestypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" @@ -12,6 +14,24 @@ import ( satypes "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types" ) +type AssetsKeeper interface { + GetAsset( + ctx sdk.Context, + assetId uint32, + ) ( + asset assettypes.Asset, + exists bool, + ) +} + +type BankKeeper interface { + GetBalance( + ctx context.Context, + addr sdk.AccAddress, + denom string, + ) sdk.Coin +} + type ClobKeeper interface { // Clob Pair. GetClobPair(ctx sdk.Context, id clobtypes.ClobPairId) (val clobtypes.ClobPair, found bool) @@ -73,6 +93,13 @@ type SendingKeeper interface { } type SubaccountsKeeper interface { + DepositFundsFromAccountToSubaccount( + ctx sdk.Context, + fromAccount sdk.AccAddress, + toSubaccountId satypes.SubaccountId, + assetId uint32, + quantums *big.Int, + ) error GetNetCollateralAndMarginRequirements( ctx sdk.Context, update satypes.Update, diff --git a/protocol/x/vault/types/megavault.go b/protocol/x/vault/types/megavault.go index 8b88855d28..9328ba7c43 100644 --- a/protocol/x/vault/types/megavault.go +++ b/protocol/x/vault/types/megavault.go @@ -6,9 +6,10 @@ import ( ) var ( + MegavaultMainAddress = authtypes.NewModuleAddress(MegavaultAccountName) // MegavaultMainSubaccount is subaccount 0 of the module account derived from string "megavault". MegavaultMainSubaccount = satypes.SubaccountId{ - Owner: authtypes.NewModuleAddress(MegavaultAccountName).String(), + Owner: MegavaultMainAddress.String(), Number: 0, } )