diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0b9fdefd1..d78169a66 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,28 @@
 # Changelog
 
+## v0.2.3-alpha.5
+This release adds 6 new features and 2 bugfixes.
+
+Features
+* [#315](https://github.com/bnb-chain/greenfield/pull/315) feat: add api for querying lock fee
+* [#290](https://github.com/bnb-chain/greenfield/pull/290) feat: replace rlp with abi.encode in cross-chain transfer
+* [#323](https://github.com/bnb-chain/greenfield/pull/323) feat: enable asset reconciliation
+* [#326](https://github.com/bnb-chain/greenfield/pull/326) feat: add bls verification
+* [#336](https://github.com/bnb-chain/greenfield/pull/336) feat: add tendermint to sdk
+* [#341](https://github.com/bnb-chain/greenfield/pull/341) feat: support cross chain for multiple blockchains
+* [#328](https://github.com/bnb-chain/greenfield/pull/328) feat: refactor for sp exit and bucket migrate 
+
+Bugfixes
+* [#307](https://github.com/bnb-chain/greenfield/pull/307) fix DefaultMaxPayloadSize from 2GB to 64GB 
+* [#312](https://github.com/bnb-chain/greenfield/pull/312) fix: add chainid to sign bytes to prevent replay attack
+
+Documentation
+* [#316](https://github.com/bnb-chain/greenfield/pull/316) Update readme.md 
+* [#282](https://github.com/bnb-chain/greenfield/pull/282) update readme for helm deployment
+
+Chores
+* [#324](https://github.com/bnb-chain/greenfield/pull/324) chore: update greenfield-cometbft-db version 
+
 ## v0.2.3-alpha.2
 This release enables 2 new features:  
 
diff --git a/Makefile b/Makefile
index 35bf7edfc..26d07f742 100644
--- a/Makefile
+++ b/Makefile
@@ -34,6 +34,9 @@ proto-format-check:
 build:
 	go build -o build/bin/gnfd -ldflags="$(ldflags)" ./cmd/gnfd/main.go
 
+mock-gen:
+	sh ./scripts/mockgen.sh
+
 docker-image:
 	go mod vendor # temporary, should be removed after open source
 	docker build . -t ${IMAGE_NAME}
diff --git a/app/ante/utils_test.go b/app/ante/utils_test.go
index db6ee42bf..46a8d434d 100644
--- a/app/ante/utils_test.go
+++ b/app/ante/utils_test.go
@@ -35,7 +35,6 @@ import (
 	govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
 	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
 	"github.com/ethereum/go-ethereum/crypto"
-	"github.com/prysmaticlabs/prysm/crypto/bls"
 	"github.com/stretchr/testify/suite"
 
 	"github.com/bnb-chain/greenfield/app"
@@ -44,6 +43,7 @@ import (
 	"github.com/bnb-chain/greenfield/e2e/core"
 	"github.com/bnb-chain/greenfield/sdk/client/test"
 	"github.com/bnb-chain/greenfield/sdk/keys"
+	"github.com/bnb-chain/greenfield/testutil/sample"
 )
 
 type AnteTestSuite struct {
@@ -96,8 +96,7 @@ func (suite *AnteTestSuite) CreateTestEIP712TxBuilderMsgDelegate(from sdk.AccAdd
 
 func (suite *AnteTestSuite) CreateTestEIP712MsgCreateValidator(from sdk.AccAddress, priv keys.KeyManager, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
 	privEd := ed25519.GenPrivKey()
-	blsSecretKey, _ := bls.RandKey()
-	blsPubkey := hex.EncodeToString(blsSecretKey.PublicKey().Marshal())
+	blsPubKey, blsProof := sample.RandBlsPubKeyAndBlsProof()
 	msgCreate, err := stakingtypes.NewMsgCreateValidator(
 		from,
 		privEd.PubKey(),
@@ -109,7 +108,8 @@ func (suite *AnteTestSuite) CreateTestEIP712MsgCreateValidator(from sdk.AccAddre
 		from,
 		from,
 		from,
-		blsPubkey,
+		blsPubKey,
+		blsProof,
 	)
 	suite.Require().NoError(err)
 	return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgCreate)
@@ -140,8 +140,7 @@ func (suite *AnteTestSuite) CreateTestEIP712GrantAllowance(from sdk.AccAddress,
 func (suite *AnteTestSuite) CreateTestEIP712MsgEditValidator(from sdk.AccAddress, priv keys.KeyManager, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
 	newRelayerAddr := core.GenRandomAddr()
 	newChallengerAddr := core.GenRandomAddr()
-	blsSecretKey, _ := bls.RandKey()
-	blsPk := hex.EncodeToString(blsSecretKey.PublicKey().Marshal())
+	blsPubKey, blsProof := sample.RandBlsPubKeyAndBlsProof()
 	msgEdit := stakingtypes.NewMsgEditValidator(
 		from,
 		stakingtypes.NewDescription("moniker", "identity", "website", "security_contract", "details"),
@@ -149,7 +148,8 @@ func (suite *AnteTestSuite) CreateTestEIP712MsgEditValidator(from sdk.AccAddress
 		nil,
 		newRelayerAddr,
 		newChallengerAddr,
-		blsPk,
+		blsPubKey,
+		blsProof,
 	)
 	return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgEdit)
 }
@@ -169,6 +169,7 @@ func (suite *AnteTestSuite) CreateTestEIP712MsgSubmitEvidence(from sdk.AccAddres
 
 func (suite *AnteTestSuite) CreateTestEIP712TxBuilderMsgSubmitProposalV1(from sdk.AccAddress, priv keys.KeyManager, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
 	privEd := ed25519.GenPrivKey()
+	blsPubKey, blsProof := sample.RandBlsPubKeyAndBlsProof()
 	msgCreate, err := stakingtypes.NewMsgCreateValidator(
 		from,
 		privEd.PubKey(),
@@ -180,7 +181,8 @@ func (suite *AnteTestSuite) CreateTestEIP712TxBuilderMsgSubmitProposalV1(from sd
 		from,
 		from,
 		from,
-		"test",
+		blsPubKey,
+		blsProof,
 	)
 	suite.Require().NoError(err)
 	msgSubmitProposal, err := govtypesv1.NewMsgSubmitProposal(
diff --git a/app/app.go b/app/app.go
index 742997d95..412727fbf 100644
--- a/app/app.go
+++ b/app/app.go
@@ -27,6 +27,7 @@ import (
 	"github.com/cosmos/cosmos-sdk/server/api"
 	"github.com/cosmos/cosmos-sdk/server/config"
 	servertypes "github.com/cosmos/cosmos-sdk/server/types"
+	"github.com/cosmos/cosmos-sdk/store/iavl"
 	storetypes "github.com/cosmos/cosmos-sdk/store/types"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	"github.com/cosmos/cosmos-sdk/types/module"
@@ -106,6 +107,9 @@ import (
 	storagemodule "github.com/bnb-chain/greenfield/x/storage"
 	storagemodulekeeper "github.com/bnb-chain/greenfield/x/storage/keeper"
 	storagemoduletypes "github.com/bnb-chain/greenfield/x/storage/types"
+	virtualgroupmodule "github.com/bnb-chain/greenfield/x/virtualgroup"
+	virtualgroupmodulekeeper "github.com/bnb-chain/greenfield/x/virtualgroup/keeper"
+	virtualgroupmoduletypes "github.com/bnb-chain/greenfield/x/virtualgroup/types"
 )
 
 const (
@@ -157,22 +161,24 @@ var (
 		spmodule.AppModuleBasic{},
 		paymentmodule.AppModuleBasic{},
 		permissionmodule.AppModuleBasic{},
+		virtualgroupmodule.AppModuleBasic{},
 		storagemodule.AppModuleBasic{},
 		challengemodule.AppModuleBasic{},
 	)
 
 	// module account permissions
 	maccPerms = map[string][]string{
-		authtypes.FeeCollectorName:       nil,
-		distrtypes.ModuleName:            nil,
-		stakingtypes.BondedPoolName:      {authtypes.Burner, authtypes.Staking},
-		stakingtypes.NotBondedPoolName:   {authtypes.Burner, authtypes.Staking},
-		govtypes.ModuleName:              {authtypes.Burner},
-		paymentmoduletypes.ModuleName:    {authtypes.Minter, authtypes.Burner, authtypes.Staking},
-		crosschaintypes.ModuleName:       {authtypes.Minter},
-		permissionmoduletypes.ModuleName: nil,
-		bridgemoduletypes.ModuleName:     nil,
-		spmoduletypes.ModuleName:         {authtypes.Staking},
+		authtypes.FeeCollectorName:         nil,
+		distrtypes.ModuleName:              nil,
+		stakingtypes.BondedPoolName:        {authtypes.Burner, authtypes.Staking},
+		stakingtypes.NotBondedPoolName:     {authtypes.Burner, authtypes.Staking},
+		govtypes.ModuleName:                {authtypes.Burner},
+		paymentmoduletypes.ModuleName:      {authtypes.Minter, authtypes.Burner, authtypes.Staking},
+		crosschaintypes.ModuleName:         {authtypes.Minter},
+		permissionmoduletypes.ModuleName:   nil,
+		bridgemoduletypes.ModuleName:       nil,
+		spmoduletypes.ModuleName:           {authtypes.Staking},
+		virtualgroupmoduletypes.ModuleName: nil,
 	}
 )
 
@@ -229,6 +235,7 @@ type App struct {
 	PaymentKeeper          paymentmodulekeeper.Keeper
 	ChallengeKeeper        challengemodulekeeper.Keeper
 	PermissionmoduleKeeper permissionmodulekeeper.Keeper
+	VirtualgroupKeeper     virtualgroupmodulekeeper.Keeper
 	StorageKeeper          storagemodulekeeper.Keeper
 
 	// mm is the module manager
@@ -283,10 +290,12 @@ func New(
 		bridgemoduletypes.StoreKey,
 		gashubtypes.StoreKey,
 		spmoduletypes.StoreKey,
+		virtualgroupmoduletypes.StoreKey,
 		paymentmoduletypes.StoreKey,
 		permissionmoduletypes.StoreKey,
 		storagemoduletypes.StoreKey,
 		challengemoduletypes.StoreKey,
+		reconStoreKey,
 	)
 	tKeys := sdk.NewTransientStoreKeys(challengemoduletypes.TStoreKey, storagemoduletypes.TStoreKey)
 	memKeys := sdk.NewMemoryStoreKeys(challengemoduletypes.MemStoreKey)
@@ -455,6 +464,18 @@ func New(
 	)
 	paymentModule := paymentmodule.NewAppModule(appCodec, app.PaymentKeeper, app.AccountKeeper, app.BankKeeper)
 
+	app.VirtualgroupKeeper = *virtualgroupmodulekeeper.NewKeeper(
+		appCodec,
+		keys[virtualgroupmoduletypes.StoreKey],
+		tKeys[virtualgroupmoduletypes.TStoreKey],
+		authtypes.NewModuleAddress(virtualgroupmoduletypes.ModuleName).String(),
+		app.SpKeeper,
+		app.AccountKeeper,
+		app.BankKeeper,
+		app.PaymentKeeper,
+	)
+	virtualgroupModule := virtualgroupmodule.NewAppModule(appCodec, app.VirtualgroupKeeper, app.SpKeeper)
+
 	app.PermissionmoduleKeeper = *permissionmodulekeeper.NewKeeper(
 		appCodec,
 		keys[permissionmoduletypes.StoreKey],
@@ -472,6 +493,7 @@ func New(
 		app.PaymentKeeper,
 		app.PermissionmoduleKeeper,
 		app.CrossChainKeeper,
+		app.VirtualgroupKeeper,
 		authtypes.NewModuleAddress(govtypes.ModuleName).String(),
 	)
 	storageModule := storagemodule.NewAppModule(appCodec, app.StorageKeeper, app.AccountKeeper, app.BankKeeper, app.SpKeeper)
@@ -519,6 +541,7 @@ func New(
 		bridgeModule,
 		gashubModule,
 		spModule,
+		virtualgroupModule,
 		paymentModule,
 		permissionModule,
 		storageModule,
@@ -547,6 +570,7 @@ func New(
 		bridgemoduletypes.ModuleName,
 		gashubtypes.ModuleName,
 		spmoduletypes.ModuleName,
+		virtualgroupmoduletypes.ModuleName,
 		paymentmoduletypes.ModuleName,
 		permissionmoduletypes.ModuleName,
 		storagemoduletypes.ModuleName,
@@ -571,6 +595,7 @@ func New(
 		bridgemoduletypes.ModuleName,
 		gashubtypes.ModuleName,
 		spmoduletypes.ModuleName,
+		virtualgroupmoduletypes.ModuleName,
 		paymentmoduletypes.ModuleName,
 		permissionmoduletypes.ModuleName,
 		storagemoduletypes.ModuleName,
@@ -600,6 +625,7 @@ func New(
 		oracletypes.ModuleName,
 		bridgemoduletypes.ModuleName,
 		spmoduletypes.ModuleName,
+		virtualgroupmoduletypes.ModuleName,
 		paymentmoduletypes.ModuleName,
 		permissionmoduletypes.ModuleName,
 		storagemoduletypes.ModuleName,
@@ -676,6 +702,18 @@ func New(
 		}
 	}
 
+	// enable diff for reconciliation
+	bankIavl, ok := ms.GetCommitStore(keys[banktypes.StoreKey]).(*iavl.Store)
+	if !ok {
+		tmos.Exit("cannot convert bank store to ival store")
+	}
+	bankIavl.EnableDiff()
+	paymentIavl, ok := ms.GetCommitStore(keys[paymentmoduletypes.StoreKey]).(*iavl.Store)
+	if !ok {
+		tmos.Exit("cannot convert payment store to ival store")
+	}
+	paymentIavl.EnableDiff()
+
 	app.initModules(ctx)
 
 	// add eth query router
@@ -695,7 +733,7 @@ func (app *App) initModules(ctx sdk.Context) {
 
 func (app *App) initCrossChain() {
 	app.CrossChainKeeper.SetSrcChainID(sdk.ChainID(app.appConfig.CrossChain.SrcChainId))
-	app.CrossChainKeeper.SetDestChainID(sdk.ChainID(app.appConfig.CrossChain.DestChainId))
+	app.CrossChainKeeper.SetDestChainID(sdk.ChainID(app.appConfig.CrossChain.DestBscChainId))
 }
 
 func (app *App) initBridge() {
@@ -716,7 +754,11 @@ func (app *App) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.R
 
 // EndBlocker application updates every end block
 func (app *App) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock {
-	return app.mm.EndBlock(ctx, req)
+	resp := app.mm.EndBlock(ctx, req)
+	bankIavl, _ := app.CommitMultiStore().GetCommitStore(sdk.NewKVStoreKey(banktypes.StoreKey)).(*iavl.Store)
+	paymentIavl, _ := app.CommitMultiStore().GetCommitStore(sdk.NewKVStoreKey(paymentmoduletypes.StoreKey)).(*iavl.Store)
+	app.reconcile(ctx, bankIavl, paymentIavl)
+	return resp
 }
 
 // InitChainer application update at chain initialization
@@ -728,12 +770,12 @@ func (app *App) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.Res
 	app.UpgradeKeeper.SetModuleVersionMap(ctx, app.mm.GetVersionMap())
 
 	// init cross chain channel permissions
-	app.CrossChainKeeper.SetChannelSendPermission(ctx, sdk.ChainID(app.appConfig.CrossChain.DestChainId), bridgemoduletypes.TransferOutChannelID, sdk.ChannelAllow)
-	app.CrossChainKeeper.SetChannelSendPermission(ctx, sdk.ChainID(app.appConfig.CrossChain.DestChainId), bridgemoduletypes.TransferInChannelID, sdk.ChannelAllow)
-	app.CrossChainKeeper.SetChannelSendPermission(ctx, sdk.ChainID(app.appConfig.CrossChain.DestChainId), bridgemoduletypes.SyncParamsChannelID, sdk.ChannelAllow)
-	app.CrossChainKeeper.SetChannelSendPermission(ctx, sdk.ChainID(app.appConfig.CrossChain.DestChainId), storagemoduletypes.BucketChannelId, sdk.ChannelAllow)
-	app.CrossChainKeeper.SetChannelSendPermission(ctx, sdk.ChainID(app.appConfig.CrossChain.DestChainId), storagemoduletypes.ObjectChannelId, sdk.ChannelAllow)
-	app.CrossChainKeeper.SetChannelSendPermission(ctx, sdk.ChainID(app.appConfig.CrossChain.DestChainId), storagemoduletypes.GroupChannelId, sdk.ChannelAllow)
+	app.CrossChainKeeper.SetChannelSendPermission(ctx, sdk.ChainID(app.appConfig.CrossChain.DestBscChainId), bridgemoduletypes.TransferOutChannelID, sdk.ChannelAllow)
+	app.CrossChainKeeper.SetChannelSendPermission(ctx, sdk.ChainID(app.appConfig.CrossChain.DestBscChainId), bridgemoduletypes.TransferInChannelID, sdk.ChannelAllow)
+	app.CrossChainKeeper.SetChannelSendPermission(ctx, sdk.ChainID(app.appConfig.CrossChain.DestBscChainId), bridgemoduletypes.SyncParamsChannelID, sdk.ChannelAllow)
+	app.CrossChainKeeper.SetChannelSendPermission(ctx, sdk.ChainID(app.appConfig.CrossChain.DestBscChainId), storagemoduletypes.BucketChannelId, sdk.ChannelAllow)
+	app.CrossChainKeeper.SetChannelSendPermission(ctx, sdk.ChainID(app.appConfig.CrossChain.DestBscChainId), storagemoduletypes.ObjectChannelId, sdk.ChannelAllow)
+	app.CrossChainKeeper.SetChannelSendPermission(ctx, sdk.ChainID(app.appConfig.CrossChain.DestBscChainId), storagemoduletypes.GroupChannelId, sdk.ChannelAllow)
 
 	return app.mm.InitGenesis(ctx, app.appCodec, genesisState)
 }
diff --git a/app/config.go b/app/config.go
index 42a81f87e..95f0949c4 100644
--- a/app/config.go
+++ b/app/config.go
@@ -13,7 +13,7 @@ type AppConfig struct {
 type CrossChainConfig struct {
 	SrcChainId uint32 `mapstructure:"src-chain-id"`
 
-	DestChainId uint32 `mapstructure:"dest-chain-id"`
+	DestBscChainId uint32 `mapstructure:"dest-bsc-chain-id"`
 }
 
 var CustomAppTemplate = serverconfig.DefaultConfigTemplate + `
@@ -23,8 +23,8 @@ var CustomAppTemplate = serverconfig.DefaultConfigTemplate + `
 [cross-chain]
 # chain-id for current chain
 src-chain-id = {{ .CrossChain.SrcChainId }}
-# chain-id for destination chain(bsc)
-dest-chain-id = {{ .CrossChain.DestChainId }}
+# chain-id for bsc destination chain
+dest-bsc-chain-id = {{ .CrossChain.DestBscChainId }}
 `
 
 func NewDefaultAppConfig() *AppConfig {
@@ -46,8 +46,8 @@ func NewDefaultAppConfig() *AppConfig {
 	return &AppConfig{
 		Config: *srvCfg,
 		CrossChain: CrossChainConfig{
-			SrcChainId:  1,
-			DestChainId: 2,
+			SrcChainId:     1,
+			DestBscChainId: 2,
 		},
 	}
 }
diff --git a/app/reconciliation.go b/app/reconciliation.go
new file mode 100644
index 000000000..cfc9334fd
--- /dev/null
+++ b/app/reconciliation.go
@@ -0,0 +1,198 @@
+package app
+
+import (
+	"bytes"
+	"encoding/binary"
+	"fmt"
+
+	"cosmossdk.io/math"
+	paymenttypes "github.com/bnb-chain/greenfield/x/payment/types"
+	"github.com/cosmos/cosmos-sdk/store/iavl"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+)
+
+const reconStoreKey = "reconciliation"
+
+// unbalancedBlockHeightKey for saving unbalanced block height for reconciliation
+var unbalancedBlockHeightKey = []byte("0x01")
+
+var (
+	SupplyKey          = banktypes.SupplyKey
+	DenomAddressPrefix = banktypes.DenomAddressPrefix
+	BalancesPrefix     = banktypes.BalancesPrefix
+
+	StreamRecordKeyPrefix = paymenttypes.StreamRecordKeyPrefix
+)
+
+// reconcile will do reconciliation for accounts balances.
+func (app *App) reconcile(ctx sdk.Context, bankIavl *iavl.Store, paymentIavl *iavl.Store) {
+	if ctx.BlockHeight() <= 2 {
+		return
+	}
+
+	height, exists := app.getUnbalancedBlockHeight(ctx)
+	if exists {
+		panic(fmt.Sprintf("unbalanced state at block height %d, please use hardfork to bypass it", height))
+	}
+
+	bankBalanced := app.reconBankChanges(ctx, bankIavl)
+	bankIavl.ResetDiff()
+
+	paymentBalanced := app.reconPaymentChanges(ctx, paymentIavl)
+	paymentIavl.ResetDiff()
+
+	if !bankBalanced || !paymentBalanced {
+		ctx.Logger().Error("reconciliation unbalanced", "bank", bankBalanced, "payment", paymentBalanced,
+			"height", ctx.BlockHeight())
+		app.saveUnbalancedBlockHeight(ctx)
+	}
+}
+
+// reconBankChanges will reconcile bank balance changes
+func (app *App) reconBankChanges(ctx sdk.Context, bankIavl *iavl.Store) bool {
+	supplyPre := sdk.Coins{}
+	balancePre := sdk.Coins{}
+	supplyCurrent := sdk.Coins{}
+	balanceCurrent := sdk.Coins{}
+
+	diff := bankIavl.GetDiff()
+	version := ctx.BlockHeight() - 2
+	ctx.Logger().Debug("reconciliation bank changes", "height", ctx.BlockHeight(), "version", version)
+	for k := range diff {
+		kBz := []byte(k)
+		denom := ""
+		isSupply := false
+		if bytes.HasPrefix(kBz, SupplyKey) {
+			isSupply = true
+			denom = parseDenomFromSupplyKey(kBz)
+			amount := math.ZeroInt()
+			if vBz := bankIavl.Get(kBz); vBz != nil {
+				amount = parseAmountFromValue(vBz)
+			}
+			supplyCurrent = supplyCurrent.Add(sdk.NewCoin(denom, amount))
+		} else if bytes.HasPrefix(kBz, BalancesPrefix) {
+			denom = parseDenomFromBalanceKey(kBz)
+			amount := math.ZeroInt()
+			if vBz := bankIavl.Get(kBz); vBz != nil {
+				amount = parseAmountFromValue(vBz)
+			}
+			balanceCurrent = balanceCurrent.Add(sdk.NewCoin(denom, amount))
+		} else {
+			continue
+		}
+
+		preStore, err := bankIavl.GetImmutable(version)
+		if err != nil {
+			panic(fmt.Sprintf("fail to find store at version %d", version))
+		}
+		vBz := preStore.Get(kBz)
+		if vBz != nil {
+			coin := sdk.NewCoin(denom, parseAmountFromValue(vBz))
+			if isSupply {
+				supplyPre = supplyPre.Add(coin)
+			} else {
+				balancePre = balancePre.Add(coin)
+			}
+		}
+	}
+
+	supplyChanges, _ := supplyCurrent.SafeSub(supplyPre...)
+	balanceChanges, _ := balanceCurrent.SafeSub(balancePre...)
+
+	ctx.Logger().Debug("reconciliation change details", "supplyCurrent", supplyCurrent, "supplyPre", supplyPre,
+		"balanceCurrent", balanceCurrent, "balancePre", balancePre,
+		"supplyChanges", supplyChanges, "balanceChanges", balanceChanges, "height", ctx.BlockHeight(), "version", version)
+	return supplyChanges.IsEqual(balanceChanges)
+}
+
+// reconPaymentChanges will reconcile payment flow rate changes
+func (app *App) reconPaymentChanges(ctx sdk.Context, paymentIavl *iavl.Store) bool {
+	flowCurrent := sdk.ZeroInt()
+	flowPre := sdk.ZeroInt()
+
+	diff := paymentIavl.GetDiff()
+	version := ctx.BlockHeight() - 2
+	ctx.Logger().Debug("reconciliation payment changes", "height", ctx.BlockHeight(), "version", version)
+	for k := range diff {
+		kBz := []byte(k)
+		if bytes.HasPrefix(kBz, StreamRecordKeyPrefix) {
+			if vBz := paymentIavl.Get(kBz); vBz != nil {
+				var sr paymenttypes.StreamRecord
+				err := app.cdc.Unmarshal(vBz, &sr)
+				if err != nil {
+					ctx.Logger().Error("fail to unmarshal stream record", "err", err.Error())
+				} else {
+					flowCurrent = flowCurrent.Add(sr.NetflowRate)
+				}
+			}
+
+			preStore, err := paymentIavl.GetImmutable(version)
+			if err != nil {
+				panic(fmt.Sprintf("fail to find store at version %d", version))
+			}
+			vBz := preStore.Get(kBz)
+			if vBz != nil {
+				var sr paymenttypes.StreamRecord
+				err = app.cdc.Unmarshal(vBz, &sr)
+				if err != nil {
+					ctx.Logger().Error("fail to unmarshal stream record", "err", err.Error())
+				} else {
+					flowPre = flowPre.Add(sr.NetflowRate)
+				}
+			}
+		}
+	}
+
+	ctx.Logger().Debug("reconciliation payment details", "flowCurrent", flowCurrent.String(), "flowPre", flowPre.String(),
+		"height", ctx.BlockHeight(), "version", version)
+	return flowCurrent.Equal(flowPre)
+}
+
+func (app *App) saveUnbalancedBlockHeight(ctx sdk.Context) {
+	reconStore := app.CommitMultiStore().GetCommitStore(sdk.NewKVStoreKey(reconStoreKey)).(*iavl.Store)
+	bz := make([]byte, 8)
+	binary.BigEndian.PutUint64(bz[:], uint64(ctx.BlockHeight()))
+	reconStore.Set(unbalancedBlockHeightKey, bz)
+}
+
+func (app *App) getUnbalancedBlockHeight(ctx sdk.Context) (uint64, bool) {
+	reconStore := app.CommitMultiStore().GetCommitStore(sdk.NewKVStoreKey(reconStoreKey)).(*iavl.Store)
+	bz := reconStore.Get(unbalancedBlockHeightKey)
+	if bz == nil {
+		return 0, false
+	}
+	return binary.BigEndian.Uint64(bz), true
+}
+
+// parseDenomFromBalanceKey parse denom from bank balance key.
+// Key format is: BalancesPrefix + Length Prefixed Address + DenomAddressPrefix + Denom + 0x00
+func parseDenomFromBalanceKey(key []byte) string {
+	l := len(key)
+	start := len(BalancesPrefix) + 1 + 20 + len(DenomAddressPrefix) - 1
+	return string(key[start:l])
+}
+
+// parseAddressFromBalanceKey parse address from bank balance key.
+// Key format is: BalancesPrefix + Length Prefixed Address + DenomAddressPrefix + Denom + 0x00
+func parseAddressFromBalanceKey(key []byte) string {
+	start := len(BalancesPrefix) + 1 // prefix-length
+	end := start + 20
+	return sdk.AccAddress(key[start:end]).String()
+}
+
+// parseDenomFromSupplyKey parse address from bank supply key.
+// Key format is: SupplyKey + Denom
+func parseDenomFromSupplyKey(key []byte) string {
+	start := len(SupplyKey)
+	return string(key[start:])
+}
+
+func parseAmountFromValue(value []byte) math.Int {
+	var amount math.Int
+	err := amount.Unmarshal(value)
+	if err != nil {
+		panic(fmt.Errorf("unable to unmarshal amount value %v", err))
+	}
+	return amount
+}
diff --git a/app/reconciliation_test.go b/app/reconciliation_test.go
new file mode 100644
index 000000000..1ed9b1a4c
--- /dev/null
+++ b/app/reconciliation_test.go
@@ -0,0 +1,23 @@
+package app
+
+import (
+	"encoding/hex"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestParseDenomFromBalanceKey(t *testing.T) {
+	key, _ := hex.DecodeString("0214cf150037e47b0c53e826a2d0050de1da2c8f5caa424e42")
+	require.Equal(t, "BNB", parseDenomFromBalanceKey(key))
+}
+
+func TestParseAddressFromBalanceKey(t *testing.T) {
+	key, _ := hex.DecodeString("0214040ffd5925d40e11c67b7238a7fc9957850b8b9a424e42")
+	require.Equal(t, "0x040fFD5925D40E11c67b7238A7fc9957850B8b9a", parseAddressFromBalanceKey(key))
+}
+
+func TestParseDenomFromSupplyKey(t *testing.T) {
+	key, _ := hex.DecodeString("00424e42")
+	require.Equal(t, "BNB", parseDenomFromSupplyKey(key))
+}
diff --git a/deployment/helm/readme.md b/deployment/helm/readme.md
new file mode 100644
index 000000000..42eac7e78
--- /dev/null
+++ b/deployment/helm/readme.md
@@ -0,0 +1,30 @@
+Helm Chart Deployment for Greenfield Validator
+
+## Dependence
+1. VMServiceScrape
+
+## Deployment
+1. `helm repo add bnb-chain https://chart.bnbchain.world/`
+2. `helm repo update`
+3. `helm install greenfield-validator bnb-chain/gnfd-validator`
+
+## Common Operations
+
+### Check Pod Status
+
+```
+$ kubectl get pod
+```
+
+You should see a `2/2` pod running for the validator. This means there are 2 containers running for the validator.
+
+To see more details about a pod, you can describe it:
+
+```
+$ kubectl describe pod <POD_NAME> 
+```
+
+### Check the Pod Logs 
+
+```
+$ kubectl logs <POD_NAME> -c <CONTAINER_NAME>
diff --git a/deployment/localup/create_sp.json b/deployment/localup/create_sp.json
index aac4782b5..450d88bd6 100644
--- a/deployment/localup/create_sp.json
+++ b/deployment/localup/create_sp.json
@@ -22,7 +22,9 @@
       "read_price": "0.087",
       "store_price": "0.0048",
       "free_read_quota": 10000000000,
-      "creator":"0x7b5Fe22B5446f7C62Ea27B8BD71CeF94e03f3dF2"
+      "creator":"0x7b5Fe22B5446f7C62Ea27B8BD71CeF94e03f3dF2",
+      "bls_key": "89f5e82d1bbb7c751b063d6d69d30c5812a088cabb9ed015b1741939a45a896d83b5435d63257b954450c26d857de4e1",
+      "bls_proof": "987406f0dd2e5f5f3e9e3e447f832dbf73809479ea552fe9b23e06e65651c01a5893e92a797d12078ef9b0c9eb72b8d90faaee65f738e222793d94b80a58cd0f329375ee04e8460f12f4772cf42859d30dd6444ed3350c3335aedf1dbde3bb68"
     }
   ],
   "title": "create sp",
diff --git a/deployment/localup/localup.sh b/deployment/localup/localup.sh
index d61362fb4..4c314d632 100644
--- a/deployment/localup/localup.sh
+++ b/deployment/localup/localup.sh
@@ -37,6 +37,7 @@ function init() {
       ${bin} keys add sp${i} --keyring-backend test --home ${workspace}/.local/sp${i} > ${workspace}/.local/sp${i}/info 2>&1
       ${bin} keys add sp${i}_fund --keyring-backend test --home ${workspace}/.local/sp${i} > ${workspace}/.local/sp${i}/fund_info 2>&1
       ${bin} keys add sp${i}_seal --keyring-backend test --home ${workspace}/.local/sp${i} > ${workspace}/.local/sp${i}/seal_info 2>&1
+      ${bin} keys add sp${i}_bls --keyring-backend test --home ${workspace}/.local/sp${i} --algo eth_bls > ${workspace}/.local/sp${i}/bls_info 2>&1
       ${bin} keys add sp${i}_approval --keyring-backend test --home ${workspace}/.local/sp${i} > ${workspace}/.local/sp${i}/approval_info 2>&1
       ${bin} keys add sp${i}_gc --keyring-backend test --home ${workspace}/.local/sp${i} > ${workspace}/.local/sp${i}/gc_info 2>&1
     done
@@ -91,9 +92,10 @@ function generate_genesis() {
         relayerAddr="$(${bin} keys show relayer${i} -a --keyring-backend test --home ${workspace}/.local/relayer${i})"
         challengerAddr="$(${bin} keys show challenger${i} -a --keyring-backend test --home ${workspace}/.local/challenger${i})"
         blsKey="$(${bin} keys show validator_bls${i} --keyring-backend test --home ${workspace}/.local/validator${i} --output json | jq -r .pubkey_hex)"
+        blsProof="$(${bin} keys sign "${blsKey}" --from validator_bls${i} --keyring-backend test --home ${workspace}/.local/validator${i})"
 
         # create bond validator tx
-        ${bin} gentx validator${i} ${STAKING_BOND_AMOUNT}${STAKING_BOND_DENOM} $validatorAddr $relayerAddr $challengerAddr $blsKey \
+        ${bin} gentx validator${i} ${STAKING_BOND_AMOUNT}${STAKING_BOND_DENOM} $validatorAddr $relayerAddr $challengerAddr $blsKey $blsProof \
             --home ${workspace}/.local/validator${i} \
             --keyring-backend=test \
             --chain-id=${CHAIN_ID} \
@@ -136,7 +138,7 @@ function generate_genesis() {
         sed -i -e "s/allow_duplicate_ip = false/allow_duplicate_ip = true/g" ${workspace}/.local/validator${i}/config/config.toml
         sed -i -e "s/snapshot-interval = 0/snapshot-interval = ${SNAPSHOT_INTERVAL}/g" ${workspace}/.local/validator${i}/config/app.toml
         sed -i -e "s/src-chain-id = 1/src-chain-id = ${SRC_CHAIN_ID}/g" ${workspace}/.local/validator${i}/config/app.toml
-        sed -i -e "s/dest-chain-id = 2/dest-chain-id = ${DEST_CHAIN_ID}/g" ${workspace}/.local/validator${i}/config/app.toml
+        sed -i -e "s/dest-bsc-chain-id = 2/dest-bsc-chain-id = ${DEST_CHAIN_ID}/g" ${workspace}/.local/validator${i}/config/app.toml
         sed -i -e "s/snapshot-keep-recent = 2/snapshot-keep-recent = ${SNAPSHOT_KEEP_RECENT}/g" ${workspace}/.local/validator${i}/config/app.toml
         sed -i -e "s/\"reserve_time\": \"15552000\"/\"reserve_time\": \"60\"/g" ${workspace}/.local/validator${i}/config/genesis.json
         sed -i -e "s/\"forced_settle_time\": \"86400\"/\"forced_settle_time\": \"30\"/g" ${workspace}/.local/validator${i}/config/genesis.json
@@ -144,12 +146,14 @@ function generate_genesis() {
         sed -i -e "s/\"10000000\"/\"${GOV_MIN_DEPOSIT_AMOUNT}\"/g" ${workspace}/.local/validator${i}/config/genesis.json
         sed -i -e "s/\"max_bytes\": \"22020096\"/\"max_bytes\": \"1048576\"/g" ${workspace}/.local/validator${i}/config/genesis.json
         sed -i -e "s/\"challenge_count_per_block\": \"1\"/\"challenge_count_per_block\": \"5\"/g" ${workspace}/.local/validator${i}/config/genesis.json
-        sed -i -e "s/\"challenge_keep_alive_period\": \"300\"/\"challenge_keep_alive_period\": \"50\"/g" ${workspace}/.local/validator${i}/config/genesis.json
+        sed -i -e "s/\"challenge_keep_alive_period\": \"300\"/\"challenge_keep_alive_period\": \"10\"/g" ${workspace}/.local/validator${i}/config/genesis.json
         sed -i -e "s/\"heartbeat_interval\": \"1000\"/\"heartbeat_interval\": \"100\"/g" ${workspace}/.local/validator${i}/config/genesis.json
         sed -i -e "s/\"attestation_inturn_interval\": \"120\"/\"attestation_inturn_interval\": \"10\"/g" ${workspace}/.local/validator${i}/config/genesis.json
         sed -i -e "s/\"discontinue_confirm_period\": \"604800\"/\"discontinue_confirm_period\": \"5\"/g" ${workspace}/.local/validator${i}/config/genesis.json
         sed -i -e "s/\"discontinue_deletion_max\": \"10000\"/\"discontinue_deletion_max\": \"1\"/g" ${workspace}/.local/validator${i}/config/genesis.json
         sed -i -e "s/\"voting_period\": \"30s\"/\"voting_period\": \"10s\"/g" ${workspace}/.local/validator${i}/config/genesis.json
+        #sed -i -e "s/\"community_tax\": \"0.020000000000000000\"/\"community_tax\": \"0\"/g" ${workspace}/.local/validator${i}/config/genesis.json
+        #sed -i -e "s/log_level = \"info\"/\log_level= \"debug\"/g" ${workspace}/.local/validator${i}/config/config.toml
     done
 
     # enable swagger API for validator0
@@ -205,6 +209,8 @@ function generate_sp_genesis {
     spoperator_addr=("$(${bin} keys show sp${i} -a --keyring-backend test --home ${workspace}/.local/sp${i})")
     spfund_addr=("$(${bin} keys show sp${i}_fund -a --keyring-backend test --home ${workspace}/.local/sp${i})")
     spseal_addr=("$(${bin} keys show sp${i}_seal -a --keyring-backend test --home ${workspace}/.local/sp${i})")
+    bls_pub_key=("$(${bin} keys show sp${i}_bls --keyring-backend test --home ${workspace}/.local/sp${i} --output json | jq -r .pubkey_hex)")
+    bls_proof=("$(${bin} keys sign "${bls_pub_key}" --from sp${i}_bls --keyring-backend test --home ${workspace}/.local/sp${i})")
     spapproval_addr=("$(${bin} keys show sp${i}_approval -a --keyring-backend test --home ${workspace}/.local/sp${i})")
     spgc_addr=("$(${bin} keys show sp${i}_gc -a --keyring-backend test --home ${workspace}/.local/sp${i})")
     validator0Addr="$(${bin} keys show validator0 -a --keyring-backend test --home ${workspace}/.local/validator0)"
@@ -215,6 +221,8 @@ function generate_sp_genesis {
       --operator-address=${spoperator_addr} \
       --funding-address=${spfund_addr} \
       --seal-address=${spseal_addr} \
+      --bls-pub-key=${bls_pub_key} \
+      --bls-proof=${bls_proof} \
       --approval-address=${spapproval_addr} \
       --gc-address=${spgc_addr} \
       --keyring-backend=test \
@@ -250,23 +258,26 @@ function export_sps {
     spseal_addr=("$(${bin} keys show sp${i}_seal -a --keyring-backend test --home ${workspace}/.local/sp${i})")
     spapproval_addr=("$(${bin} keys show sp${i}_approval -a --keyring-backend test --home ${workspace}/.local/sp${i})")
     spgc_addr=("$(${bin} keys show sp${i}_gc -a --keyring-backend test --home ${workspace}/.local/sp${i})")
-
+    bls_pub_key=("$(${bin} keys show sp${i}_bls --keyring-backend test --home ${workspace}/.local/sp${i} --output json | jq -r .pubkey_hex)")
     spoperator_priv_key=("$(echo "y" | ${bin} keys export sp${i} --unarmored-hex --unsafe --keyring-backend test --home ${workspace}/.local/sp${i})")
     spfund_priv_key=("$(echo "y" | ${bin} keys export sp${i}_fund --unarmored-hex --unsafe --keyring-backend test --home ${workspace}/.local/sp${i})")
     spseal_priv_key=("$(echo "y" | ${bin} keys export sp${i}_seal --unarmored-hex --unsafe --keyring-backend test --home ${workspace}/.local/sp${i})")
     spapproval_priv_key=("$(echo "y" | ${bin} keys export sp${i}_approval --unarmored-hex --unsafe --keyring-backend test --home ${workspace}/.local/sp${i})")
     spgc_priv_key=("$(echo "y" | ${bin} keys export sp${i}_gc --unarmored-hex --unsafe --keyring-backend test --home ${workspace}/.local/sp${i})")
+    bls_priv_key=("$(echo "y" | ${bin} keys export sp${i}_bls --unarmored-hex --unsafe --keyring-backend test --home ${workspace}/.local/sp${i})")
     output="${output}\"sp${i}\":{"
     output="${output}\"OperatorAddress\": \"${spoperator_addr}\","
     output="${output}\"FundingAddress\": \"${spfund_addr}\","
     output="${output}\"SealAddress\": \"${spseal_addr}\","
     output="${output}\"ApprovalAddress\": \"${spapproval_addr}\","
     output="${output}\"GcAddress\": \"${spgc_addr}\","
+    output="${output}\"BlsPubKey\": \"${bls_pub_key}\","
     output="${output}\"OperatorPrivateKey\": \"${spoperator_priv_key}\","
     output="${output}\"FundingPrivateKey\": \"${spfund_priv_key}\","
     output="${output}\"SealPrivateKey\": \"${spseal_priv_key}\","
     output="${output}\"ApprovalPrivateKey\": \"${spapproval_priv_key}\","
-    output="${output}\"GcPrivateKey\": \"${spgc_priv_key}\""
+    output="${output}\"GcPrivateKey\": \"${spgc_priv_key}\","
+    output="${output}\"BlsPrivateKey\": \"${bls_priv_key}\""
     output="${output}},"
   done
   output="${output%?}}"
diff --git a/deployment/readme.md b/deployment/readme.md
index b2356afa1..546995e53 100644
--- a/deployment/readme.md
+++ b/deployment/readme.md
@@ -49,13 +49,14 @@ VALIDATOR=$(./build/bin/gnfd keys show validator -a --keyring-backend test)
 RELAYER=$(./build/bin/gnfd keys show relayer -a --keyring-backend test)
 CHALLENGER=$(./build/bin/gnfd keys show challenger -a --keyring-backend test)
 BLS=$(./build/bin/gnfd keys show bls --keyring-backend test --output json | jq -r .pubkey_hex)
+BLS_PROOF=$(./build/bin/gnfd keys sign ${BLS} --keyring-backend test --from validator)
 ./build/bin/gnfd add-genesis-account $VALIDATOR 100000000000000000000000000BNB
 ```
 
 5. Create validator in genesis state
 ```bash
 # create a gentx.
-./build/bin/gnfd gentx validator 10000000000000000000000000BNB $VALIDATOR $RELAYER $CHALLENGER $BLS --keyring-backend=test --chain-id=greenfield_9000-1 \
+./build/bin/gnfd gentx validator 10000000000000000000000000BNB $VALIDATOR $RELAYER $CHALLENGER $BLS $BLS_PROOF --keyring-backend=test --chain-id=greenfield_9000-1 \
     --moniker="validator" \
     --commission-max-change-rate=0.01 \
     --commission-max-rate=1.0 \
@@ -85,7 +86,7 @@ bash ./deployment/localup/localup.sh stop
 
 3. Send Tx
 ```bash
-./build/bin/gnfd tx bank send validator0 0x32Ff14Fa1547314b95991976DB432F9Aa648A423 500000000000000000000BNB --home ./deployment/localup/.local/validator0 --keyring-backend test --node http://localhost:26750 -b block
+./build/bin/gnfd tx bank send validator0 0x32Ff14Fa1547314b95991976DB432F9Aa648A423 500000000000000000000BNB --home ./deployment/localup/.local/validator0 --keyring-backend test --node http://localhost:26750 -b sync
 ```
 
 4. Restart the chain without state initialization
diff --git a/e2e/core/basesuite.go b/e2e/core/basesuite.go
index 1196f400b..86a57cccf 100644
--- a/e2e/core/basesuite.go
+++ b/e2e/core/basesuite.go
@@ -1,29 +1,45 @@
 package core
 
 import (
+	"bytes"
 	"context"
+	"encoding/hex"
 	"errors"
 	"fmt"
+	"math"
+	"strconv"
 	"strings"
 	"time"
 
-	"cosmossdk.io/math"
+	sdkmath "cosmossdk.io/math"
+	"github.com/cometbft/cometbft/crypto/tmhash"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	"github.com/cosmos/cosmos-sdk/types/tx"
+	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
+	"github.com/cosmos/cosmos-sdk/x/authz"
 	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+	gov "github.com/cosmos/cosmos-sdk/x/gov/types"
+	govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
+	"github.com/prysmaticlabs/prysm/crypto/bls"
 	"github.com/stretchr/testify/suite"
 
 	"github.com/bnb-chain/greenfield/sdk/client"
 	"github.com/bnb-chain/greenfield/sdk/keys"
 	"github.com/bnb-chain/greenfield/sdk/types"
+	sptypes "github.com/bnb-chain/greenfield/x/sp/types"
+	storagetypes "github.com/bnb-chain/greenfield/x/storage/types"
+	virtualgroupmoduletypes "github.com/bnb-chain/greenfield/x/virtualgroup/types"
 )
 
-type SPKeyManagers struct {
-	OperatorKey keys.KeyManager
-	SealKey     keys.KeyManager
-	FundingKey  keys.KeyManager
-	ApprovalKey keys.KeyManager
-	GcKey       keys.KeyManager
+type StorageProvider struct {
+	OperatorKey                keys.KeyManager
+	SealKey                    keys.KeyManager
+	FundingKey                 keys.KeyManager
+	ApprovalKey                keys.KeyManager
+	GcKey                      keys.KeyManager
+	BlsKey                     keys.KeyManager
+	Info                       *sptypes.StorageProvider
+	GlobalVirtualGroupFamilies map[uint32][]*virtualgroupmoduletypes.GlobalVirtualGroup
 }
 
 type BaseSuite struct {
@@ -35,7 +51,7 @@ type BaseSuite struct {
 	ValidatorBLS     keys.KeyManager
 	Relayer          keys.KeyManager
 	Challenger       keys.KeyManager
-	StorageProviders []SPKeyManagers
+	StorageProviders []StorageProvider
 }
 
 func (s *BaseSuite) SetupSuite() {
@@ -52,19 +68,69 @@ func (s *BaseSuite) SetupSuite() {
 	s.Require().NoError(err)
 	s.Challenger, err = keys.NewMnemonicKeyManager(s.Config.ChallengerMnemonic)
 	s.Require().NoError(err)
-	for _, spMnemonics := range s.Config.SPMnemonics {
-		sPKeyManagers := SPKeyManagers{}
-		sPKeyManagers.OperatorKey, err = keys.NewMnemonicKeyManager(spMnemonics.OperatorMnemonic)
+
+	var spIDs []uint32
+	for i, spMnemonics := range s.Config.SPMnemonics {
+		sp := StorageProvider{}
+		sp.OperatorKey, err = keys.NewMnemonicKeyManager(spMnemonics.OperatorMnemonic)
+		s.Require().NoError(err)
+		sp.SealKey, err = keys.NewMnemonicKeyManager(spMnemonics.SealMnemonic)
 		s.Require().NoError(err)
-		sPKeyManagers.SealKey, err = keys.NewMnemonicKeyManager(spMnemonics.SealMnemonic)
+		sp.FundingKey, err = keys.NewMnemonicKeyManager(spMnemonics.FundingMnemonic)
 		s.Require().NoError(err)
-		sPKeyManagers.FundingKey, err = keys.NewMnemonicKeyManager(spMnemonics.FundingMnemonic)
+		sp.ApprovalKey, err = keys.NewMnemonicKeyManager(spMnemonics.ApprovalMnemonic)
 		s.Require().NoError(err)
-		sPKeyManagers.ApprovalKey, err = keys.NewMnemonicKeyManager(spMnemonics.ApprovalMnemonic)
+		sp.GcKey, err = keys.NewMnemonicKeyManager(spMnemonics.GcMnemonic)
 		s.Require().NoError(err)
-		sPKeyManagers.GcKey, err = keys.NewMnemonicKeyManager(spMnemonics.GcMnemonic)
+		sp.BlsKey, err = keys.NewBlsMnemonicKeyManager(s.Config.SPBLSMnemonic[i])
+		s.Require().NoError(err)
+		var resp *sptypes.QueryStorageProviderByOperatorAddressResponse
+		resp, err = s.Client.StorageProviderByOperatorAddress(context.Background(), &sptypes.QueryStorageProviderByOperatorAddressRequest{
+			OperatorAddress: sp.OperatorKey.GetAddr().String(),
+		})
 		s.Require().NoError(err)
-		s.StorageProviders = append(s.StorageProviders, sPKeyManagers)
+		sp.Info = resp.StorageProvider
+		sp.GlobalVirtualGroupFamilies = make(map[uint32][]*virtualgroupmoduletypes.GlobalVirtualGroup)
+		s.StorageProviders = append(s.StorageProviders, sp)
+
+		spIDs = append(spIDs, sp.Info.Id)
+	}
+
+	for i, sp := range s.StorageProviders {
+		var gvgFamilies []*virtualgroupmoduletypes.GlobalVirtualGroupFamily
+		resp1, err1 := s.Client.GlobalVirtualGroupFamilies(context.Background(), &virtualgroupmoduletypes.QueryGlobalVirtualGroupFamiliesRequest{StorageProviderId: sp.Info.Id})
+		s.Require().NoError(err1)
+		if len(resp1.GlobalVirtualGroupFamilies) == 0 {
+			// Create a GVG for each sp by default
+			deposit := sdk.Coin{
+				Denom:  s.Config.Denom,
+				Amount: types.NewIntFromInt64WithDecimal(1, types.DecimalBNB),
+			}
+			secondaryIds := append(spIDs[:i], spIDs[i+1:]...)
+			msgCreateGVG := &virtualgroupmoduletypes.MsgCreateGlobalVirtualGroup{
+				StorageProvider: sp.OperatorKey.GetAddr().String(),
+				SecondarySpIds:  secondaryIds,
+				Deposit:         deposit,
+			}
+			s.SendTxBlock(sp.OperatorKey, msgCreateGVG)
+			resp2, err2 := s.Client.GlobalVirtualGroupFamilies(context.Background(), &virtualgroupmoduletypes.QueryGlobalVirtualGroupFamiliesRequest{StorageProviderId: sp.Info.Id})
+			s.Require().NoError(err2)
+
+			gvgFamilies = resp2.GlobalVirtualGroupFamilies
+		} else {
+			gvgFamilies = resp1.GlobalVirtualGroupFamilies
+
+		}
+
+		for _, family := range gvgFamilies {
+			gvgsResp, err3 := s.Client.GlobalVirtualGroupByFamilyID(context.Background(), &virtualgroupmoduletypes.QueryGlobalVirtualGroupByFamilyIDRequest{
+				StorageProviderId:          sp.Info.Id,
+				GlobalVirtualGroupFamilyId: family.Id,
+			})
+			s.Require().NoError(err3)
+			sp.GlobalVirtualGroupFamilies[family.Id] = gvgsResp.GlobalVirtualGroups
+			s.StorageProviders[i] = sp
+		}
 	}
 }
 
@@ -145,7 +211,7 @@ func (s *BaseSuite) GenAndChargeAccounts(n int, balance int64) (accounts []keys.
 	}
 	// prevent int64 multiplication overflow
 	balanceInt := types.NewIntFromInt64WithDecimal(balance, types.DecimalBNB)
-	nInt := math.NewInt(int64(n))
+	nInt := sdkmath.NewInt(int64(n))
 	in := banktypes.Input{
 		Address: s.Validator.GetAddr().String(),
 		Coins:   []sdk.Coin{{Denom: denom, Amount: balanceInt.Mul(nInt)}},
@@ -158,6 +224,18 @@ func (s *BaseSuite) GenAndChargeAccounts(n int, balance int64) (accounts []keys.
 	return accounts
 }
 
+func (s *BaseSuite) GenRandomBlsKeyManager() keys.KeyManager {
+	blsPrivKey, err := bls.RandKey()
+	if err != nil {
+		panic("failed to init bls key")
+	}
+	km, err := keys.NewBlsPrivateKeyManager(hex.EncodeToString(blsPrivKey.Marshal()))
+	if err != nil {
+		panic("failed to init bls key manager")
+	}
+	return km
+}
+
 func (s *BaseSuite) CheckTxCode(txHash string, expectedCode uint32) error {
 	// wait for 2 blocks
 	for i := 0; i < 2; i++ {
@@ -220,6 +298,25 @@ func (s *BaseSuite) WaitForHeightWithTimeout(h int64, t time.Duration) (int64, e
 	}
 }
 
+func (s *BaseSuite) WaitForTx(hash string) (*sdk.TxResponse, error) {
+	for {
+		txResponse, err := s.Client.GetTx(context.Background(), &tx.GetTxRequest{Hash: hash})
+		if err != nil {
+			if strings.Contains(err.Error(), "not found") {
+				// Tx not found, wait for next block and try again
+				err := s.WaitForNextBlock()
+				if err != nil {
+					return nil, err
+				}
+				continue
+			}
+			return nil, err
+		}
+		// Tx found
+		return txResponse.TxResponse, nil
+	}
+}
+
 func (s *BaseSuite) LatestHeight() (int64, error) {
 	ticker := time.NewTicker(100 * time.Millisecond)
 	defer ticker.Stop()
@@ -242,3 +339,288 @@ func (s *BaseSuite) LatestHeight() (int64, error) {
 		}
 	}
 }
+
+func (sp *StorageProvider) GetFirstGlobalVirtualGroup() (*virtualgroupmoduletypes.GlobalVirtualGroup, bool) {
+	for _, family := range sp.GlobalVirtualGroupFamilies {
+		if len(family) != 0 {
+			return family[0], true
+		}
+	}
+	return nil, false
+}
+
+func (s *BaseSuite) NewSpAcc() *StorageProvider {
+	userAccs := s.GenAndChargeAccounts(5, 1000000)
+	operatorAcc := userAccs[0]
+	fundingAcc := userAccs[1]
+	approvalAcc := userAccs[2]
+	sealAcc := userAccs[3]
+	gcAcc := userAccs[4]
+
+	blsKm := s.GenRandomBlsKeyManager()
+	return &StorageProvider{OperatorKey: operatorAcc, SealKey: fundingAcc,
+		FundingKey: approvalAcc, ApprovalKey: sealAcc, GcKey: gcAcc, BlsKey: blsKm}
+}
+
+func (s *BaseSuite) CreateNewStorageProvider() *StorageProvider {
+	validator := s.Validator.GetAddr()
+
+	// 1. create new newStorageProvider
+	newSP := s.NewSpAcc()
+
+	// 2. grant deposit authorization of sp to gov module account
+	coins := sdk.NewCoin(s.Config.Denom, types.NewIntFromInt64WithDecimal(10000, types.DecimalBNB))
+	authorization := sptypes.NewDepositAuthorization(newSP.OperatorKey.GetAddr(), &coins)
+
+	govAddr := authtypes.NewModuleAddress(gov.ModuleName)
+	now := time.Now().Add(24 * time.Hour)
+	grantMsg, err := authz.NewMsgGrant(
+		newSP.FundingKey.GetAddr(), govAddr, authorization, &now)
+	s.Require().NoError(err)
+	s.SendTxBlock(newSP.FundingKey, grantMsg)
+
+	// 2. submit CreateStorageProvider proposal
+	deposit := sdk.Coin{
+		Denom:  s.Config.Denom,
+		Amount: types.NewIntFromInt64WithDecimal(10000, types.DecimalBNB),
+	}
+	description := sptypes.Description{
+		Moniker:  "sp_test",
+		Identity: "",
+	}
+
+	endpoint := "http://127.0.0.1:9034"
+	newReadPrice := sdk.NewDec(RandInt64(100, 200))
+	newStorePrice := sdk.NewDec(RandInt64(10000, 20000))
+
+	// bls pub key
+	newSpBlsKm := newSP.BlsKey
+	blsProofBz, err := newSpBlsKm.Sign(tmhash.Sum(newSpBlsKm.PubKey().Bytes()))
+	s.Require().NoError(err)
+
+	msgCreateSP, _ := sptypes.NewMsgCreateStorageProvider(govAddr,
+		newSP.OperatorKey.GetAddr(), newSP.FundingKey.GetAddr(),
+		newSP.SealKey.GetAddr(),
+		newSP.ApprovalKey.GetAddr(),
+		newSP.GcKey.GetAddr(), description,
+		endpoint, deposit, newReadPrice, 10000, newStorePrice,
+		hex.EncodeToString(newSP.BlsKey.PubKey().Bytes()),
+		hex.EncodeToString(blsProofBz),
+	)
+
+	msgProposal, err := govtypesv1.NewMsgSubmitProposal(
+		[]sdk.Msg{msgCreateSP},
+		sdk.Coins{sdk.NewCoin(s.Config.Denom, types.NewIntFromInt64WithDecimal(100, types.DecimalBNB))},
+		validator.String(),
+		"test", "test", "test",
+	)
+	s.Require().NoError(err)
+
+	txRes := s.SendTxBlock(s.Validator, msgProposal)
+	s.Require().Equal(txRes.Code, uint32(0))
+
+	// 3. query proposal and get proposal ID
+	var proposalId uint64
+	for _, event := range txRes.Logs[0].Events {
+		if event.Type == "submit_proposal" {
+			for _, attr := range event.Attributes {
+				if attr.Key == "proposal_id" {
+					proposalId, err = strconv.ParseUint(attr.Value, 10, 0)
+					s.Require().NoError(err)
+					break
+				}
+			}
+			break
+		}
+	}
+	s.Require().True(proposalId != 0)
+
+	queryProposal := &govtypesv1.QueryProposalRequest{ProposalId: proposalId}
+	_, err = s.Client.GovQueryClientV1.Proposal(context.Background(), queryProposal)
+	s.Require().NoError(err)
+
+	// 4. submit MsgVote and wait the proposal exec
+	msgVote := govtypesv1.NewMsgVote(validator, proposalId, govtypesv1.OptionYes, "test")
+	txRes = s.SendTxBlock(s.Validator, msgVote)
+	s.Require().Equal(txRes.Code, uint32(0))
+
+	queryVoteParamsReq := govtypesv1.QueryParamsRequest{ParamsType: "voting"}
+	queryVoteParamsResp, err := s.Client.GovQueryClientV1.Params(context.Background(), &queryVoteParamsReq)
+	s.Require().NoError(err)
+
+	// 5. wait a voting period and confirm that the proposal success.
+	s.T().Logf("voting period %s", *queryVoteParamsResp.Params.VotingPeriod)
+	time.Sleep(*queryVoteParamsResp.Params.VotingPeriod)
+	time.Sleep(1 * time.Second)
+	proposalRes, err := s.Client.GovQueryClientV1.Proposal(context.Background(), queryProposal)
+	s.Require().NoError(err)
+	s.Require().Equal(proposalRes.Proposal.Status, govtypesv1.ProposalStatus_PROPOSAL_STATUS_PASSED)
+
+	// 6. query storage provider
+	querySPByOperatorAddrReq := sptypes.QueryStorageProviderByOperatorAddressRequest{
+		OperatorAddress: newSP.OperatorKey.GetAddr().String(),
+	}
+	querySPByOperatorAddrResp, err := s.Client.StorageProviderByOperatorAddress(context.Background(), &querySPByOperatorAddrReq)
+	s.Require().NoError(err)
+	s.Require().Equal(querySPByOperatorAddrResp.StorageProvider.OperatorAddress, newSP.OperatorKey.GetAddr().String())
+	s.Require().Equal(querySPByOperatorAddrResp.StorageProvider.FundingAddress, newSP.FundingKey.GetAddr().String())
+	s.Require().Equal(querySPByOperatorAddrResp.StorageProvider.SealAddress, newSP.SealKey.GetAddr().String())
+	s.Require().Equal(querySPByOperatorAddrResp.StorageProvider.ApprovalAddress, newSP.ApprovalKey.GetAddr().String())
+	s.Require().Equal(querySPByOperatorAddrResp.StorageProvider.Endpoint, endpoint)
+	newSP.Info = querySPByOperatorAddrResp.StorageProvider
+	return newSP
+}
+
+func (s *BaseSuite) CreateObject(user keys.KeyManager, primarySP *StorageProvider, gvgID uint32, bucketName, objectName string) (secondarySps []*StorageProvider, familyID, resGVGID uint32, bucketInfo storagetypes.BucketInfo) {
+	// GetGVG
+	resp, err := s.Client.GlobalVirtualGroup(
+		context.Background(),
+		&virtualgroupmoduletypes.QueryGlobalVirtualGroupRequest{GlobalVirtualGroupId: gvgID})
+	s.Require().NoError(err)
+	gvg := resp.GlobalVirtualGroup
+
+	// CreateBucket
+	msgCreateBucket := storagetypes.NewMsgCreateBucket(
+		user.GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PRIVATE, primarySP.OperatorKey.GetAddr(),
+		nil, math.MaxUint, nil, 0)
+	msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
+	msgCreateBucket.PrimarySpApproval.Sig, err = primarySP.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
+	s.Require().NoError(err)
+	s.SendTxBlock(user, msgCreateBucket)
+
+	// HeadBucket
+	ctx := context.Background()
+	queryHeadBucketRequest := storagetypes.QueryHeadBucketRequest{
+		BucketName: bucketName,
+	}
+	queryHeadBucketResponse, err := s.Client.HeadBucket(ctx, &queryHeadBucketRequest)
+	s.Require().NoError(err)
+	s.Require().Equal(queryHeadBucketResponse.BucketInfo.BucketName, bucketName)
+	bucketInfo = *queryHeadBucketResponse.BucketInfo
+
+	// create test buffer
+	var buffer bytes.Buffer
+	line := `1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,
+	1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,
+	1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,
+	1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,
+	1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,
+	1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,
+	1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,
+	1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,
+	1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,
+	1234567890,1234567890,1234567890,123`
+	// Create 1MiB content where each line contains 1024 characters.
+	for i := 0; i < 1024; i++ {
+		buffer.WriteString(fmt.Sprintf("[%05d] %s\n", i, line))
+	}
+	payloadSize := buffer.Len()
+	checksum := sdk.Keccak256(buffer.Bytes())
+	expectChecksum := [][]byte{checksum, checksum, checksum, checksum, checksum, checksum, checksum}
+	contextType := "text/event-stream"
+	msgCreateObject := storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, objectName, uint64(payloadSize), storagetypes.VISIBILITY_TYPE_PRIVATE, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil)
+	msgCreateObject.PrimarySpApproval.Sig, err = primarySP.ApprovalKey.Sign(msgCreateObject.GetApprovalBytes())
+	s.Require().NoError(err)
+	s.SendTxBlock(user, msgCreateObject)
+
+	// HeadObject
+	queryHeadObjectRequest := storagetypes.QueryHeadObjectRequest{
+		BucketName: bucketName,
+		ObjectName: objectName,
+	}
+	queryHeadObjectResponse, err := s.Client.HeadObject(ctx, &queryHeadObjectRequest)
+	s.Require().NoError(err)
+	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.ObjectName, objectName)
+	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.BucketName, bucketName)
+
+	// query gvg
+	queryGlobalvirtualGroupResp, err := s.Client.GlobalVirtualGroup(ctx, &virtualgroupmoduletypes.QueryGlobalVirtualGroupRequest{
+		GlobalVirtualGroupId: gvgID,
+	})
+	s.Require().NoError(err)
+	originGVG := queryGlobalvirtualGroupResp.GlobalVirtualGroup
+	// SealObject
+	gvgId := gvg.Id
+	msgSealObject := storagetypes.NewMsgSealObject(primarySP.SealKey.GetAddr(), bucketName, objectName, gvg.Id, nil)
+
+	secondarySigs := make([][]byte, 0)
+	secondarySPBlsPubKeys := make([]bls.PublicKey, 0)
+	blsSignHash := storagetypes.NewSecondarySpSealObjectSignDoc(s.GetChainID(), gvgId, queryHeadObjectResponse.ObjectInfo.Id, storagetypes.GenerateHash(queryHeadObjectResponse.ObjectInfo.Checksums[:])).GetBlsSignHash()
+	// every secondary sp signs the checksums
+	for i := 1; i < len(s.StorageProviders); i++ {
+		sig, err := BlsSignAndVerify(s.StorageProviders[i], blsSignHash)
+		s.Require().NoError(err)
+		secondarySigs = append(secondarySigs, sig)
+		pk, err := bls.PublicKeyFromBytes(s.StorageProviders[i].BlsKey.PubKey().Bytes())
+		s.Require().NoError(err)
+		secondarySPBlsPubKeys = append(secondarySPBlsPubKeys, pk)
+		if s.StorageProviders[i].Info.Id != primarySP.Info.Id {
+			ssp := s.StorageProviders[i]
+			secondarySps = append(secondarySps, &ssp)
+		}
+	}
+	aggBlsSig, err := BlsAggregateAndVerify(secondarySPBlsPubKeys, blsSignHash, secondarySigs)
+	s.Require().NoError(err)
+	msgSealObject.SecondarySpBlsAggSignatures = aggBlsSig
+	s.SendTxBlock(primarySP.SealKey, msgSealObject)
+
+	queryHeadObjectResponse, err = s.Client.HeadObject(ctx, &queryHeadObjectRequest)
+	s.Require().NoError(err)
+	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.ObjectName, objectName)
+	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.BucketName, bucketName)
+	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.ObjectStatus, storagetypes.OBJECT_STATUS_SEALED)
+
+	// verify gvg store size
+	queryGlobalvirtualGroupResp, err = s.Client.GlobalVirtualGroup(ctx, &virtualgroupmoduletypes.QueryGlobalVirtualGroupRequest{
+		GlobalVirtualGroupId: gvgID,
+	})
+	s.Require().NoError(err)
+	s.Require().Equal(originGVG.StoredSize+uint64(payloadSize), queryGlobalvirtualGroupResp.GlobalVirtualGroup.StoredSize)
+
+	return secondarySps, gvg.FamilyId, gvg.Id, bucketInfo
+}
+
+func (s *BaseSuite) CreateGlobalVirtualGroup(sp *StorageProvider, familyID uint32, secondarySPIDs []uint32, depositAmount int64) (uint32, uint32) {
+	// Create a GVG for each sp by default
+	deposit := sdk.Coin{
+		Denom:  s.Config.Denom,
+		Amount: types.NewIntFromInt64WithDecimal(depositAmount, types.DecimalBNB),
+	}
+	msgCreateGVG := &virtualgroupmoduletypes.MsgCreateGlobalVirtualGroup{
+		StorageProvider: sp.OperatorKey.GetAddr().String(),
+		SecondarySpIds:  secondarySPIDs,
+		Deposit:         deposit,
+		FamilyId:        familyID,
+	}
+	resp := s.SendTxBlock(sp.OperatorKey, msgCreateGVG)
+
+	// wait for the tx execute
+	resp2, err := s.WaitForTx(resp.TxHash)
+	s.Require().NoError(err)
+
+	var gvgID uint32
+	var newFamilyID uint32
+	for _, e := range resp2.Events {
+		s.T().Logf("Event: %s", e.String())
+		if e.Type == "greenfield.virtualgroup.EventCreateGlobalVirtualGroup" {
+			for _, a := range e.Attributes {
+				if a.Key == "id" {
+					num, err := strconv.ParseUint(a.Value, 10, 32)
+					s.Require().NoError(err)
+					gvgID = uint32(num)
+				}
+				if a.Key == "family_id" {
+					num, err := strconv.ParseUint(a.Value, 10, 32)
+					s.Require().NoError(err)
+					newFamilyID = uint32(num)
+				}
+			}
+		}
+	}
+	s.T().Logf("gvgID: %d, familyID: %d", gvgID, newFamilyID)
+	return gvgID, newFamilyID
+}
+
+func (s *BaseSuite) GetChainID() string {
+	return s.Config.ChainId
+}
diff --git a/e2e/core/config.go b/e2e/core/config.go
index c044e898e..f2e59bbe3 100644
--- a/e2e/core/config.go
+++ b/e2e/core/config.go
@@ -24,6 +24,7 @@ type Config struct {
 	RelayerMnemonic      string        `yaml:"RelayerMnemonic"`    // relayer mnemonic
 	ChallengerMnemonic   string        `yaml:"ChallengerMnemonic"` // challenger mnemonic
 	SPMnemonics          []SPMnemonics `yaml:"SPMnemonics"`
+	SPBLSMnemonic        []string      `yaml:"SPBLSMnemonic"`
 	Denom                string        `yaml:"Denom"`
 }
 
@@ -45,6 +46,7 @@ func InitE2eConfig() *Config {
 	}
 	for i := 0; i < 7; i++ {
 		config.SPMnemonics = append(config.SPMnemonics, ParseSPMnemonics(i))
+		config.SPBLSMnemonic = append(config.SPBLSMnemonic, ParseSPBLSMnemonics(i))
 	}
 	return config
 }
@@ -78,7 +80,11 @@ func ParseSPMnemonics(i int) SPMnemonics {
 		ApprovalMnemonic: ParseMnemonicFromFile(fmt.Sprintf("../../deployment/localup/.local/sp%d/approval_info", i)),
 		GcMnemonic:       ParseMnemonicFromFile(fmt.Sprintf("../../deployment/localup/.local/sp%d/gc_info", i)),
 	}
+}
 
+// ParseSPBLSMnemonics read the sp bls mnemonics from file
+func ParseSPBLSMnemonics(i int) string {
+	return ParseMnemonicFromFile(fmt.Sprintf("../../deployment/localup/.local/sp%d/bls_info", i))
 }
 
 func ParseMnemonicFromFile(fileName string) string {
diff --git a/e2e/core/utils.go b/e2e/core/utils.go
index c5aa77f0b..2042c8d44 100644
--- a/e2e/core/utils.go
+++ b/e2e/core/utils.go
@@ -7,9 +7,11 @@ import (
 
 	"github.com/cometbft/cometbft/crypto"
 	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/prysmaticlabs/prysm/crypto/bls"
 	"sigs.k8s.io/yaml"
 
 	"github.com/bnb-chain/greenfield/sdk/keys"
+	gnfdtypes "github.com/bnb-chain/greenfield/types"
 )
 
 func GenRandomAddr() sdk.AccAddress {
@@ -47,3 +49,32 @@ func YamlString(data interface{}) string {
 func RandInt64(min, max int64) int64 {
 	return min + rand.Int63n(max-min)
 }
+
+func BlsSignAndVerify(sp StorageProvider, signBz [32]byte) ([]byte, error) {
+	secondarySig, err := sp.BlsKey.Sign(signBz[:])
+	if err != nil {
+		return nil, err
+	}
+	pubKey, err := bls.PublicKeyFromBytes(sp.BlsKey.PubKey().Bytes())
+	if err != nil {
+		return nil, err
+	}
+	err = gnfdtypes.VerifyBlsSignature(pubKey, signBz, secondarySig)
+	if err != nil {
+		return nil, err
+	}
+	return secondarySig, nil
+}
+
+func BlsAggregateAndVerify(secondarySPBlsPubKeys []bls.PublicKey, signBz [32]byte, secondarySigs [][]byte) ([]byte, error) {
+	blsSigs, err := bls.MultipleSignaturesFromBytes(secondarySigs)
+	if err != nil {
+		return nil, err
+	}
+	aggBlsSig := bls.AggregateSignatures(blsSigs).Marshal()
+	err = gnfdtypes.VerifyBlsAggSignature(secondarySPBlsPubKeys, signBz, aggBlsSig)
+	if err != nil {
+		return nil, err
+	}
+	return aggBlsSig, nil
+}
diff --git a/e2e/tests/bridge_test.go b/e2e/tests/bridge_test.go
index b26320cd1..4d0d457c3 100644
--- a/e2e/tests/bridge_test.go
+++ b/e2e/tests/bridge_test.go
@@ -42,7 +42,7 @@ func (s *BridgeTestSuite) TestTransferOut() {
 	params, err := s.Client.BridgeQueryClient.Params(ctx, &bridgetypes.QueryParamsRequest{})
 	s.Require().NoError(err)
 
-	totalTransferOutRelayerFee := params.Params.TransferOutRelayerFee.Add(params.Params.TransferOutAckRelayerFee)
+	totalTransferOutRelayerFee := params.Params.BscTransferOutRelayerFee.Add(params.Params.BscTransferOutAckRelayerFee)
 
 	moduleAccount := types.MustAccAddressFromHex("0xB73C0Aac4C1E606C6E495d848196355e6CB30381")
 	// query balance before
diff --git a/e2e/tests/challenge_test.go b/e2e/tests/challenge_test.go
index 117532a9e..b42a91619 100644
--- a/e2e/tests/challenge_test.go
+++ b/e2e/tests/challenge_test.go
@@ -15,6 +15,7 @@ import (
 	"github.com/bits-and-blooms/bitset"
 	ctypes "github.com/cometbft/cometbft/rpc/core/types"
 	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/prysmaticlabs/prysm/crypto/bls"
 	"github.com/stretchr/testify/suite"
 
 	"github.com/bnb-chain/greenfield/e2e/core"
@@ -38,15 +39,18 @@ func TestChallengeTestSuite(t *testing.T) {
 	suite.Run(t, new(ChallengeTestSuite))
 }
 
-func (s *ChallengeTestSuite) createObject() (string, string, sdk.AccAddress, []sdk.AccAddress) {
+func (s *ChallengeTestSuite) createObject() (string, string, sdk.AccAddress) {
 	var err error
 	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
 	// CreateBucket
 	user := s.GenAndChargeAccounts(1, 1000000)[0]
 	bucketName := "ch" + storagetestutil.GenRandomBucketName()
 	msgCreateBucket := storagetypes.NewMsgCreateBucket(
 		user.GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PRIVATE, sp.OperatorKey.GetAddr(),
 		nil, math.MaxUint, nil, 0)
+	msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
 	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user, msgCreateBucket)
@@ -82,7 +86,7 @@ func (s *ChallengeTestSuite) createObject() (string, string, sdk.AccAddress, []s
 	checksum := sdk.Keccak256(buffer.Bytes())
 	expectChecksum := [][]byte{checksum, checksum, checksum, checksum, checksum, checksum, checksum}
 	contextType := "text/event-stream"
-	msgCreateObject := storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, objectName, uint64(payloadSize), storagetypes.VISIBILITY_TYPE_PRIVATE, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil, nil)
+	msgCreateObject := storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, objectName, uint64(payloadSize), storagetypes.VISIBILITY_TYPE_PRIVATE, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil)
 	msgCreateObject.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateObject.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user, msgCreateObject)
@@ -98,22 +102,24 @@ func (s *ChallengeTestSuite) createObject() (string, string, sdk.AccAddress, []s
 	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.BucketName, bucketName)
 
 	// SealObject
-	secondarySPs := []sdk.AccAddress{
-		sp.OperatorKey.GetAddr(), sp.OperatorKey.GetAddr(),
-		sp.OperatorKey.GetAddr(), sp.OperatorKey.GetAddr(),
-		sp.OperatorKey.GetAddr(), sp.OperatorKey.GetAddr(),
+	gvgId := gvg.Id
+	msgSealObject := storagetypes.NewMsgSealObject(sp.SealKey.GetAddr(), bucketName, objectName, gvg.Id, nil)
+
+	secondarySigs := make([][]byte, 0)
+	secondarySPBlsPubKeys := make([]bls.PublicKey, 0)
+	blsSignHash := storagetypes.NewSecondarySpSealObjectSignDoc(s.GetChainID(), gvgId, queryHeadObjectResponse.ObjectInfo.Id, storagetypes.GenerateHash(queryHeadObjectResponse.ObjectInfo.Checksums[:])).GetBlsSignHash()
+	// every secondary sp signs the checksums
+	for i := 1; i < len(s.StorageProviders); i++ {
+		sig, err := core.BlsSignAndVerify(s.StorageProviders[i], blsSignHash)
+		s.Require().NoError(err)
+		secondarySigs = append(secondarySigs, sig)
+		pk, err := bls.PublicKeyFromBytes(s.StorageProviders[i].BlsKey.PubKey().Bytes())
+		s.Require().NoError(err)
+		secondarySPBlsPubKeys = append(secondarySPBlsPubKeys, pk)
 	}
-	msgSealObject := storagetypes.NewMsgSealObject(sp.SealKey.GetAddr(), bucketName, objectName, secondarySPs, nil)
-	sr := storagetypes.NewSecondarySpSignDoc(sp.OperatorKey.GetAddr(), queryHeadObjectResponse.ObjectInfo.Id, checksum)
-	secondarySig, err := sp.ApprovalKey.Sign(sr.GetSignBytes())
-	s.Require().NoError(err)
-	err = storagetypes.VerifySignature(sp.ApprovalKey.GetAddr(), sdk.Keccak256(sr.GetSignBytes()), secondarySig)
+	aggBlsSig, err := core.BlsAggregateAndVerify(secondarySPBlsPubKeys, blsSignHash, secondarySigs)
 	s.Require().NoError(err)
-
-	s.Require().NoError(err)
-
-	secondarySigs := [][]byte{secondarySig, secondarySig, secondarySig, secondarySig, secondarySig, secondarySig}
-	msgSealObject.SecondarySpSignatures = secondarySigs
+	msgSealObject.SecondarySpBlsAggSignatures = aggBlsSig
 	s.SendTxBlock(sp.SealKey, msgSealObject)
 
 	queryHeadObjectResponse, err = s.Client.HeadObject(ctx, &queryHeadObjectRequest)
@@ -122,13 +128,13 @@ func (s *ChallengeTestSuite) createObject() (string, string, sdk.AccAddress, []s
 	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.BucketName, bucketName)
 	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.ObjectStatus, storagetypes.OBJECT_STATUS_SEALED)
 
-	return bucketName, objectName, sp.OperatorKey.GetAddr(), secondarySPs
+	return bucketName, objectName, sp.OperatorKey.GetAddr()
 }
 
 func (s *ChallengeTestSuite) TestSubmit() {
 	user := s.GenAndChargeAccounts(1, 1000000)[0]
 
-	bucketName, objectName, primarySp, _ := s.createObject()
+	bucketName, objectName, primarySp := s.createObject()
 	msgSubmit := challengetypes.NewMsgSubmit(user.GetAddr(), primarySp, bucketName, objectName, true, 1000)
 	txRes := s.SendTxBlock(user, msgSubmit)
 	event := filterChallengeEventFromTx(txRes) // secondary sps are faked with primary sp, redundancy check is meaningless here
@@ -136,13 +142,12 @@ func (s *ChallengeTestSuite) TestSubmit() {
 	s.Require().NotEqual(event.SegmentIndex, uint32(100))
 	s.Require().Equal(event.SpOperatorAddress, primarySp.String())
 
-	bucketName, objectName, _, secondarySps := s.createObject()
-	msgSubmit = challengetypes.NewMsgSubmit(user.GetAddr(), secondarySps[0], bucketName, objectName, false, 0)
+	bucketName, objectName, _ = s.createObject()
+	msgSubmit = challengetypes.NewMsgSubmit(user.GetAddr(), s.StorageProviders[0].OperatorKey.GetAddr(), bucketName, objectName, false, 0)
 	txRes = s.SendTxBlock(user, msgSubmit)
 	event = filterChallengeEventFromTx(txRes)
 	s.Require().GreaterOrEqual(event.ChallengeId, uint64(0))
 	s.Require().Equal(event.SegmentIndex, uint32(0))
-	s.Require().Equal(event.SpOperatorAddress, secondarySps[0].String())
 }
 
 func (s *ChallengeTestSuite) calculateValidatorBitSet(height int64, blsKey string) *bitset.BitSet {
@@ -167,7 +172,7 @@ func (s *ChallengeTestSuite) calculateValidatorBitSet(height int64, blsKey strin
 func (s *ChallengeTestSuite) TestNormalAttest() {
 	user := s.GenAndChargeAccounts(1, 1000000)[0]
 
-	bucketName, objectName, primarySp, _ := s.createObject()
+	bucketName, objectName, primarySp := s.createObject()
 	msgSubmit := challengetypes.NewMsgSubmit(user.GetAddr(), primarySp, bucketName, objectName, true, 1000)
 	txRes := s.SendTxBlock(user, msgSubmit)
 	event := filterChallengeEventFromTx(txRes)
@@ -180,7 +185,7 @@ func (s *ChallengeTestSuite) TestNormalAttest() {
 
 	msgAttest := challengetypes.NewMsgAttest(s.Challenger.GetAddr(), event.ChallengeId, event.ObjectId, primarySp.String(),
 		challengetypes.CHALLENGE_SUCCEED, user.GetAddr().String(), valBitset.Bytes(), nil)
-	toSign := msgAttest.GetBlsSignBytes()
+	toSign := msgAttest.GetBlsSignBytes(s.Config.ChainId)
 
 	voteAggSignature, err := s.ValidatorBLS.Sign(toSign[:])
 	if err != nil {
@@ -261,7 +266,7 @@ func (s *ChallengeTestSuite) TestHeartbeatAttest() {
 
 	msgAttest := challengetypes.NewMsgAttest(s.Challenger.GetAddr(), event.ChallengeId, event.ObjectId,
 		event.SpOperatorAddress, challengetypes.CHALLENGE_FAILED, "", valBitset.Bytes(), nil)
-	toSign := msgAttest.GetBlsSignBytes()
+	toSign := msgAttest.GetBlsSignBytes(s.Config.ChainId)
 
 	voteAggSignature, err := s.ValidatorBLS.Sign(toSign[:])
 	if err != nil {
@@ -304,7 +309,7 @@ func (s *ChallengeTestSuite) TestHeartbeatAttest() {
 func (s *ChallengeTestSuite) TestFailedAttest_ChallengeExpired() {
 	user := s.GenAndChargeAccounts(1, 1000000)[0]
 
-	bucketName, objectName, primarySp, _ := s.createObject()
+	bucketName, objectName, primarySp := s.createObject()
 	msgSubmit := challengetypes.NewMsgSubmit(user.GetAddr(), primarySp, bucketName, objectName, true, 1000)
 	txRes := s.SendTxBlock(user, msgSubmit)
 	event := filterChallengeEventFromTx(txRes)
@@ -331,7 +336,7 @@ func (s *ChallengeTestSuite) TestFailedAttest_ChallengeExpired() {
 
 	msgAttest := challengetypes.NewMsgAttest(user.GetAddr(), event.ChallengeId, event.ObjectId, primarySp.String(),
 		challengetypes.CHALLENGE_SUCCEED, user.GetAddr().String(), valBitset.Bytes(), nil)
-	toSign := msgAttest.GetBlsSignBytes()
+	toSign := msgAttest.GetBlsSignBytes(s.Config.ChainId)
 
 	voteAggSignature, err := s.ValidatorBLS.Sign(toSign[:])
 	if err != nil {
diff --git a/e2e/tests/eip712_test.go b/e2e/tests/eip712_test.go
index 843a2308c..739d712c4 100644
--- a/e2e/tests/eip712_test.go
+++ b/e2e/tests/eip712_test.go
@@ -30,6 +30,8 @@ func TestEip712TestSuite(t *testing.T) {
 func (s *Eip712TestSuite) TestMultiMessages() {
 	var err error
 	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
 	user := s.GenAndChargeAccounts(1, 1000000)[0]
 
 	// CreateBucket
@@ -37,6 +39,7 @@ func (s *Eip712TestSuite) TestMultiMessages() {
 	msgCreateBucket := storagetypes.NewMsgCreateBucket(
 		user.GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PUBLIC_READ, sp.OperatorKey.GetAddr(),
 		nil, math.MaxUint, nil, 0)
+	msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
 	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
 	s.Require().NoError(err)
 
diff --git a/e2e/tests/gensp_test.go b/e2e/tests/gensp_test.go
index 7954a4030..84737473c 100644
--- a/e2e/tests/gensp_test.go
+++ b/e2e/tests/gensp_test.go
@@ -24,11 +24,11 @@ func (s *GenStorageProviderTestSuite) TestGenStorageProvider() {
 
 	sp := s.StorageProviders[0]
 
-	querySPReq := sptypes.QueryStorageProviderRequest{
-		SpAddress: sp.OperatorKey.GetAddr().String(),
+	querySPReq := sptypes.QueryStorageProviderByOperatorAddressRequest{
+		OperatorAddress: sp.OperatorKey.GetAddr().String(),
 	}
 
-	querySPResp, err := s.Client.StorageProvider(ctx, &querySPReq)
+	querySPResp, err := s.Client.StorageProviderByOperatorAddress(ctx, &querySPReq)
 
 	genSP := &sptypes.StorageProvider{
 		OperatorAddress: sp.OperatorKey.GetAddr().String(),
@@ -36,6 +36,7 @@ func (s *GenStorageProviderTestSuite) TestGenStorageProvider() {
 		SealAddress:     sp.SealKey.GetAddr().String(),
 		ApprovalAddress: sp.ApprovalKey.GetAddr().String(),
 		GcAddress:       sp.GcKey.GetAddr().String(),
+		BlsKey:          sp.BlsKey.PubKey().Bytes(),
 		Description: sptypes.Description{
 			Moniker:  "sp0",
 			Identity: "",
@@ -47,6 +48,7 @@ func (s *GenStorageProviderTestSuite) TestGenStorageProvider() {
 	}
 
 	s.Require().NoError(err)
+	genSP.Id = querySPResp.StorageProvider.Id
 	s.Require().Equal(querySPResp.StorageProvider, genSP)
 }
 
diff --git a/e2e/tests/payment_test.go b/e2e/tests/payment_test.go
index cce1f0435..a01607503 100644
--- a/e2e/tests/payment_test.go
+++ b/e2e/tests/payment_test.go
@@ -5,25 +5,40 @@ import (
 	"context"
 	"fmt"
 	"math"
+	"sort"
 	"strconv"
 	"testing"
 	"time"
 
+	sdkmath "cosmossdk.io/math"
 	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/cosmos/cosmos-sdk/types/tx"
 	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
 	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
 	govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
 	govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
+	"github.com/prysmaticlabs/prysm/crypto/bls"
+	"github.com/samber/lo"
 	"github.com/stretchr/testify/suite"
 
 	"github.com/bnb-chain/greenfield/e2e/core"
 	"github.com/bnb-chain/greenfield/sdk/keys"
 	"github.com/bnb-chain/greenfield/sdk/types"
-	storagetestutil "github.com/bnb-chain/greenfield/testutil/storage"
+	storagetestutils "github.com/bnb-chain/greenfield/testutil/storage"
+	"github.com/bnb-chain/greenfield/types/common"
 	paymenttypes "github.com/bnb-chain/greenfield/x/payment/types"
+	sptypes "github.com/bnb-chain/greenfield/x/sp/types"
 	storagetypes "github.com/bnb-chain/greenfield/x/storage/types"
+	virtualgroupmoduletypes "github.com/bnb-chain/greenfield/x/virtualgroup/types"
 )
 
+type StreamRecords struct {
+	User      paymenttypes.StreamRecord
+	GVGFamily paymenttypes.StreamRecord
+	GVG       paymenttypes.StreamRecord
+	Tax       paymenttypes.StreamRecord
+}
+
 type PaymentTestSuite struct {
 	core.BaseSuite
 }
@@ -142,23 +157,28 @@ func (s *PaymentTestSuite) updateParams(params paymenttypes.Params) {
 	s.Require().NoError(err)
 	s.Require().Equal(queryParamsResponse.Params.VersionedParams.ReserveTime,
 		queryParamsByTimestampResponse.Params.VersionedParams.ReserveTime)
+	s.T().Logf("new params: %s", params.String())
 }
 
-func (s *PaymentTestSuite) createObject() (keys.KeyManager, string, string, storagetypes.Uint, []byte) {
+func (s *PaymentTestSuite) createBucketAndObject() (keys.KeyManager, string, string, storagetypes.Uint, [][]byte) {
 	var err error
 	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
+
 	// CreateBucket
 	user := s.GenAndChargeAccounts(1, 1000000)[0]
-	bucketName := "ch" + storagetestutil.GenRandomBucketName()
+	bucketName := "ch" + storagetestutils.GenRandomBucketName()
 	msgCreateBucket := storagetypes.NewMsgCreateBucket(
 		user.GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PRIVATE, sp.OperatorKey.GetAddr(),
-		nil, math.MaxUint, nil, 10000)
+		nil, math.MaxUint, nil, 0)
+	msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
 	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user, msgCreateBucket)
 
 	// CreateObject
-	objectName := storagetestutil.GenRandomObjectName()
+	objectName := storagetestutils.GenRandomObjectName()
 	// create test buffer
 	var buffer bytes.Buffer
 	line := `1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,
@@ -179,7 +199,9 @@ func (s *PaymentTestSuite) createObject() (keys.KeyManager, string, string, stor
 	checksum := sdk.Keccak256(buffer.Bytes())
 	expectChecksum := [][]byte{checksum, checksum, checksum, checksum, checksum, checksum, checksum}
 	contextType := "text/event-stream"
-	msgCreateObject := storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, objectName, uint64(payloadSize), storagetypes.VISIBILITY_TYPE_PRIVATE, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil, nil)
+	msgCreateObject := storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, objectName, uint64(payloadSize),
+		storagetypes.VISIBILITY_TYPE_PRIVATE, expectChecksum, contextType,
+		storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil)
 	msgCreateObject.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateObject.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user, msgCreateObject)
@@ -194,28 +216,63 @@ func (s *PaymentTestSuite) createObject() (keys.KeyManager, string, string, stor
 	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.ObjectName, objectName)
 	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.BucketName, bucketName)
 
-	return user, bucketName, objectName, queryHeadObjectResponse.ObjectInfo.Id, checksum
+	return user, bucketName, objectName, queryHeadObjectResponse.ObjectInfo.Id, expectChecksum
 }
 
-func (s *PaymentTestSuite) sealObject(bucketName, objectName string, objectId storagetypes.Uint, checksum []byte) {
+func (s *PaymentTestSuite) createBucket() (keys.KeyManager, string) {
+	var err error
 	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
 
-	secondarySPs := []sdk.AccAddress{
-		sp.OperatorKey.GetAddr(), sp.OperatorKey.GetAddr(),
-		sp.OperatorKey.GetAddr(), sp.OperatorKey.GetAddr(),
-		sp.OperatorKey.GetAddr(), sp.OperatorKey.GetAddr(),
-	}
-	msgSealObject := storagetypes.NewMsgSealObject(sp.SealKey.GetAddr(), bucketName, objectName, secondarySPs, nil)
-	sr := storagetypes.NewSecondarySpSignDoc(sp.OperatorKey.GetAddr(), objectId, checksum)
-	secondarySig, err := sp.ApprovalKey.Sign(sr.GetSignBytes())
-	s.Require().NoError(err)
-	err = storagetypes.VerifySignature(sp.ApprovalKey.GetAddr(), sdk.Keccak256(sr.GetSignBytes()), secondarySig)
+	// CreateBucket
+	user := s.GenAndChargeAccounts(1, 1000000)[0]
+	bucketName := "ch" + storagetestutils.GenRandomBucketName()
+	msgCreateBucket := storagetypes.NewMsgCreateBucket(
+		user.GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PRIVATE, sp.OperatorKey.GetAddr(),
+		nil, math.MaxUint, nil, 0)
+	msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
+	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
 	s.Require().NoError(err)
+	s.SendTxBlock(user, msgCreateBucket)
 
-	secondarySigs := [][]byte{secondarySig, secondarySig, secondarySig, secondarySig, secondarySig, secondarySig}
-	msgSealObject.SecondarySpSignatures = secondarySigs
-	s.SendTxBlock(sp.SealKey, msgSealObject)
+	return user, bucketName
+}
+
+func (s *PaymentTestSuite) createObject(user keys.KeyManager, bucketName string) (keys.KeyManager, string, string, storagetypes.Uint, [][]byte) {
+	var err error
+	sp := s.StorageProviders[0]
 
+	// CreateObject
+	objectName := storagetestutils.GenRandomObjectName()
+	// create test buffer
+	var buffer bytes.Buffer
+	line := `1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,
+	1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,
+	1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,
+	1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,
+	1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,
+	1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,
+	1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,
+	1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,
+	1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,
+	1234567890,1234567890,1234567890,123`
+	// Create 1MiB content where each line contains 1024 characters.
+	for i := 0; i < 1024; i++ {
+		buffer.WriteString(fmt.Sprintf("[%05d] %s\n", i, line))
+	}
+	payloadSize := buffer.Len()
+	checksum := sdk.Keccak256(buffer.Bytes())
+	expectChecksum := [][]byte{checksum, checksum, checksum, checksum, checksum, checksum, checksum}
+	contextType := "text/event-stream"
+	msgCreateObject := storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, objectName, uint64(payloadSize),
+		storagetypes.VISIBILITY_TYPE_PRIVATE, expectChecksum, contextType,
+		storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil)
+	msgCreateObject.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateObject.GetApprovalBytes())
+	s.Require().NoError(err)
+	s.SendTxBlock(user, msgCreateObject)
+
+	// HeadObject
 	queryHeadObjectRequest := storagetypes.QueryHeadObjectRequest{
 		BucketName: bucketName,
 		ObjectName: objectName,
@@ -224,7 +281,46 @@ func (s *PaymentTestSuite) sealObject(bucketName, objectName string, objectId st
 	s.Require().NoError(err)
 	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.ObjectName, objectName)
 	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.BucketName, bucketName)
-	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.ObjectStatus, storagetypes.OBJECT_STATUS_SEALED)
+
+	return user, bucketName, objectName, queryHeadObjectResponse.ObjectInfo.Id, expectChecksum
+}
+
+func (s *PaymentTestSuite) sealObject(bucketName, objectName string, objectId storagetypes.Uint, checksums [][]byte) {
+	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
+	s.T().Log("GVG info: ", gvg.String())
+
+	// SealObject
+	gvgId := gvg.Id
+	msgSealObject := storagetypes.NewMsgSealObject(sp.SealKey.GetAddr(), bucketName, objectName, gvg.Id, nil)
+	secondarySigs := make([][]byte, 0)
+	secondarySPBlsPubKeys := make([]bls.PublicKey, 0)
+	blsSignHash := storagetypes.NewSecondarySpSealObjectSignDoc(s.GetChainID(), gvgId, objectId, storagetypes.GenerateHash(checksums[:])).GetBlsSignHash()
+	// every secondary sp signs the checksums
+	for i := 1; i < len(s.StorageProviders); i++ {
+		sig, err := core.BlsSignAndVerify(s.StorageProviders[i], blsSignHash)
+		s.Require().NoError(err)
+		secondarySigs = append(secondarySigs, sig)
+		pk, err := bls.PublicKeyFromBytes(s.StorageProviders[i].BlsKey.PubKey().Bytes())
+		s.Require().NoError(err)
+		secondarySPBlsPubKeys = append(secondarySPBlsPubKeys, pk)
+	}
+	aggBlsSig, err := core.BlsAggregateAndVerify(secondarySPBlsPubKeys, blsSignHash, secondarySigs)
+	s.Require().NoError(err)
+	msgSealObject.SecondarySpBlsAggSignatures = aggBlsSig
+	s.T().Logf("msg %s", msgSealObject.String())
+	s.SendTxBlock(sp.SealKey, msgSealObject)
+
+	queryHeadObjectRequest2 := storagetypes.QueryHeadObjectRequest{
+		BucketName: bucketName,
+		ObjectName: objectName,
+	}
+	queryHeadObjectResponse2, err := s.Client.HeadObject(context.Background(), &queryHeadObjectRequest2)
+	s.Require().NoError(err)
+	s.Require().Equal(queryHeadObjectResponse2.ObjectInfo.ObjectName, objectName)
+	s.Require().Equal(queryHeadObjectResponse2.ObjectInfo.BucketName, bucketName)
+	s.Require().Equal(queryHeadObjectResponse2.ObjectInfo.ObjectStatus, storagetypes.OBJECT_STATUS_SEALED)
 }
 
 // TestVersionedParams_SealAfterReserveTimeChange will cover the following case:
@@ -236,7 +332,7 @@ func (s *PaymentTestSuite) TestVersionedParams_SealObjectAfterReserveTimeChange(
 	s.Require().NoError(err)
 
 	// create bucket, create object
-	user, bucketName, objectName, objectId, checksum := s.createObject()
+	user, bucketName, objectName, objectId, checksums := s.createBucketAndObject()
 
 	// update params
 	params := queryParamsResponse.GetParams()
@@ -254,7 +350,7 @@ func (s *PaymentTestSuite) TestVersionedParams_SealObjectAfterReserveTimeChange(
 	s.T().Logf("params, ReserveTime: %d, ValidatorTaxRate: %s", params.VersionedParams.ReserveTime, params.VersionedParams.ValidatorTaxRate)
 
 	// seal object
-	s.sealObject(bucketName, objectName, objectId, checksum)
+	s.sealObject(bucketName, objectName, objectId, checksums)
 
 	queryHeadObjectRequest := storagetypes.QueryHeadObjectRequest{
 		BucketName: bucketName,
@@ -287,17 +383,22 @@ func (s *PaymentTestSuite) TestVersionedParams_DeleteBucketAfterValidatorTaxRate
 	queryParamsResponse, err := s.Client.PaymentQueryClient.Params(ctx, &queryParamsRequest)
 	s.Require().NoError(err)
 
+	validatorTaxPoolRate := sdk.ZeroInt()
 	queryStreamRequest := paymenttypes.QueryGetStreamRecordRequest{Account: paymenttypes.ValidatorTaxPoolAddress.String()}
 	queryStreamResponse, err := s.Client.PaymentQueryClient.StreamRecord(ctx, &queryStreamRequest)
-	s.Require().NoError(err)
-	validatorTaxPoolRate := queryStreamResponse.StreamRecord.NetflowRate
+	if err != nil {
+		s.Require().ErrorContains(err, "key not found")
+	} else {
+		s.Require().NoError(err)
+		validatorTaxPoolRate = queryStreamResponse.StreamRecord.NetflowRate
+	}
 	s.T().Logf("netflow, validatorTaxPoolRate: %s", validatorTaxPoolRate)
 
 	// create bucket, create object
-	user, bucketName, objectName, objectId, checksum := s.createObject()
+	user, bucketName, objectName, objectId, checksums := s.createBucketAndObject()
 
 	// seal object
-	s.sealObject(bucketName, objectName, objectId, checksum)
+	s.sealObject(bucketName, objectName, objectId, checksums)
 
 	// update params
 	params := queryParamsResponse.GetParams()
@@ -341,10 +442,10 @@ func (s *PaymentTestSuite) TestVersionedParams_DeleteObjectAfterReserveTimeChang
 	s.Require().NoError(err)
 
 	// create bucket, create object
-	user, bucketName, objectName, objectId, checksum := s.createObject()
+	user, bucketName, objectName, objectId, checksums := s.createBucketAndObject()
 
 	// seal object
-	s.sealObject(bucketName, objectName, objectId, checksum)
+	s.sealObject(bucketName, objectName, objectId, checksums)
 
 	// for payment
 	time.Sleep(2 * time.Second)
@@ -422,6 +523,949 @@ func (s *PaymentTestSuite) TestVersionedParams_DeleteObjectAfterReserveTimeChang
 	s.updateParams(params)
 }
 
+func (s *PaymentTestSuite) TestDepositAndResume_InOneBlock() {
+	ctx := context.Background()
+	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
+	user := s.GenAndChargeAccounts(1, 1000000)[0]
+	userAddr := user.GetAddr().String()
+	var err error
+
+	params, err := s.Client.PaymentQueryClient.Params(ctx, &paymenttypes.QueryParamsRequest{})
+	s.T().Logf("params %s, err: %v", params, err)
+	s.Require().NoError(err)
+	reserveTime := params.Params.VersionedParams.ReserveTime
+	queryGetSpStoragePriceByTimeResp, err := s.Client.QueryGetSpStoragePriceByTime(ctx, &sptypes.QueryGetSpStoragePriceByTimeRequest{
+		SpAddr: sp.OperatorKey.GetAddr().String(),
+	})
+	s.T().Logf("queryGetSpStoragePriceByTimeResp %s, err: %v", queryGetSpStoragePriceByTimeResp, err)
+	s.Require().NoError(err)
+
+	bucketChargedReadQuota := uint64(1000)
+	readPrice := queryGetSpStoragePriceByTimeResp.SpStoragePrice.ReadPrice
+	totalUserRate := readPrice.MulInt(sdkmath.NewIntFromUint64(bucketChargedReadQuota)).TruncateInt()
+	taxRateParam := params.Params.VersionedParams.ValidatorTaxRate
+	taxStreamRate := taxRateParam.MulInt(totalUserRate).TruncateInt()
+	expectedRate := totalUserRate.Add(taxStreamRate)
+	paymentAccountBNBNeeded := expectedRate.Mul(sdkmath.NewIntFromUint64(reserveTime))
+
+	// create payment account and deposit
+	msgCreatePaymentAccount := &paymenttypes.MsgCreatePaymentAccount{
+		Creator: userAddr,
+	}
+	_ = s.SendTxBlock(user, msgCreatePaymentAccount)
+	paymentAccountsReq := &paymenttypes.QueryGetPaymentAccountsByOwnerRequest{Owner: userAddr}
+	paymentAccounts, err := s.Client.PaymentQueryClient.GetPaymentAccountsByOwner(ctx, paymentAccountsReq)
+	s.Require().NoError(err)
+	s.T().Logf("paymentAccounts %s", core.YamlString(paymentAccounts))
+	paymentAddr := paymentAccounts.PaymentAccounts[0]
+	s.Require().Lenf(paymentAccounts.PaymentAccounts, 1, "paymentAccounts %s", core.YamlString(paymentAccounts))
+
+	// deposit BNB needed
+	msgDeposit := &paymenttypes.MsgDeposit{
+		Creator: user.GetAddr().String(),
+		To:      paymentAddr,
+		Amount:  paymentAccountBNBNeeded,
+	}
+	_ = s.SendTxBlock(user, msgDeposit)
+
+	// create bucket
+	bucketName := storagetestutils.GenRandomBucketName()
+	msgCreateBucket := storagetypes.NewMsgCreateBucket(
+		user.GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PUBLIC_READ, sp.OperatorKey.GetAddr(),
+		sdk.MustAccAddressFromHex(paymentAddr), math.MaxUint, nil, bucketChargedReadQuota)
+	msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
+	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
+	s.Require().NoError(err)
+	s.SendTxBlock(user, msgCreateBucket)
+
+	// check payment account stream record
+	paymentAccountStreamRecord := s.GetStreamRecord(paymentAddr)
+	s.T().Logf("paymentAccountStreamRecord %s", core.YamlString(paymentAccountStreamRecord))
+	s.Require().Equal(expectedRate.String(), paymentAccountStreamRecord.NetflowRate.Neg().String())
+	s.Require().Equal(paymentAccountStreamRecord.BufferBalance.String(), paymentAccountBNBNeeded.String())
+	s.Require().Equal(paymentAccountStreamRecord.StaticBalance.String(), sdkmath.ZeroInt().String())
+
+	// wait until settle time
+	retryCount := 0
+	for {
+		latestBlock, err := s.TmClient.TmClient.Block(ctx, nil)
+		s.Require().NoError(err)
+		currentTimestamp := latestBlock.Block.Time.Unix()
+		s.T().Logf("currentTimestamp %d, paymentAccountStreamRecord.SettleTimestamp %d", currentTimestamp, paymentAccountStreamRecord.SettleTimestamp)
+		if currentTimestamp > paymentAccountStreamRecord.SettleTimestamp {
+			break
+		}
+		time.Sleep(time.Second)
+		retryCount++
+		if retryCount > 60 {
+			s.T().Fatalf("wait for settle time timeout")
+		}
+	}
+	// check auto settle
+	paymentStreamRecordAfterAutoSettle := s.GetStreamRecord(paymentAddr)
+	s.T().Logf("paymentStreamRecordAfterAutoSettle %s", core.YamlString(paymentStreamRecordAfterAutoSettle))
+	s.Require().NotEqual(paymentStreamRecordAfterAutoSettle.Status, paymenttypes.STREAM_ACCOUNT_STATUS_ACTIVE)
+
+	// deposit, balance not enough to resume
+	depositAmount1 := sdk.NewInt(1)
+	msgDeposit1 := &paymenttypes.MsgDeposit{
+		Creator: userAddr,
+		To:      paymentAddr,
+		Amount:  depositAmount1,
+	}
+	_ = s.SendTxBlock(user, msgDeposit1)
+
+	// check payment account stream record
+	paymentAccountStreamRecordAfterDeposit1 := s.GetStreamRecord(paymentAddr)
+	s.T().Logf("paymentAccountStreamRecordAfterDeposit1 %s", core.YamlString(paymentAccountStreamRecordAfterDeposit1))
+	s.Require().NotEqual(paymentAccountStreamRecordAfterDeposit1.Status, paymenttypes.STREAM_ACCOUNT_STATUS_ACTIVE)
+
+	// deposit and resume
+	depositAmount2 := sdk.NewInt(1e10)
+	msgDeposit2 := &paymenttypes.MsgDeposit{
+		Creator: userAddr,
+		To:      paymentAddr,
+		Amount:  depositAmount2,
+	}
+	s.SendTxBlock(user, msgDeposit2)
+	// check payment account stream record
+	paymentAccountStreamRecordAfterDeposit2 := s.GetStreamRecord(paymentAddr)
+	s.T().Logf("paymentAccountStreamRecordAfterDeposit2 %s", core.YamlString(paymentAccountStreamRecordAfterDeposit2))
+	s.Require().Equal(paymentAccountStreamRecordAfterDeposit2.Status, paymenttypes.STREAM_ACCOUNT_STATUS_ACTIVE)
+	s.Require().Equal(paymentAccountStreamRecordAfterDeposit2.StaticBalance.Add(paymentAccountStreamRecordAfterDeposit2.BufferBalance).String(), paymentAccountStreamRecordAfterDeposit1.StaticBalance.Add(depositAmount2).String())
+}
+
+func (s *PaymentTestSuite) TestDepositAndResume_InBlocks() {
+	ctx := context.Background()
+	// update params
+	queryParamsRequest := paymenttypes.QueryParamsRequest{}
+	queryParamsResponse, err := s.Client.PaymentQueryClient.Params(ctx, &queryParamsRequest)
+	s.Require().NoError(err)
+	params := queryParamsResponse.GetParams()
+	oldMaxAutoResumeFlowCount := params.MaxAutoResumeFlowCount
+	s.T().Logf("params, MaxAutoResumeFlowCount: %d", oldMaxAutoResumeFlowCount)
+
+	params.MaxAutoResumeFlowCount = 1 // update to 1
+	s.updateParams(params)
+	queryParamsResponse, err = s.Client.PaymentQueryClient.Params(ctx, &queryParamsRequest)
+	s.Require().NoError(err)
+	params = queryParamsResponse.GetParams()
+	s.T().Logf("params: %s", params.String())
+
+	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
+	user := s.GenAndChargeAccounts(1, 1000000)[0]
+	userAddr := user.GetAddr().String()
+
+	reserveTime := params.VersionedParams.ReserveTime
+	queryGetSpStoragePriceByTimeResp, err := s.Client.QueryGetSpStoragePriceByTime(ctx, &sptypes.QueryGetSpStoragePriceByTimeRequest{
+		SpAddr: sp.OperatorKey.GetAddr().String(),
+	})
+	s.T().Logf("queryGetSpStoragePriceByTimeResp %s, err: %v", queryGetSpStoragePriceByTimeResp, err)
+	s.Require().NoError(err)
+
+	bucketChargedReadQuota := uint64(1000)
+	readPrice := queryGetSpStoragePriceByTimeResp.SpStoragePrice.ReadPrice
+	totalUserRate := readPrice.MulInt(sdkmath.NewIntFromUint64(bucketChargedReadQuota)).TruncateInt()
+	taxRateParam := params.VersionedParams.ValidatorTaxRate
+	taxStreamRate := taxRateParam.MulInt(totalUserRate).TruncateInt()
+	expectedRate := totalUserRate.Add(taxStreamRate)
+	paymentAccountBNBNeeded := expectedRate.Mul(sdkmath.NewIntFromUint64(reserveTime))
+
+	// create payment account and deposit
+	msgCreatePaymentAccount := &paymenttypes.MsgCreatePaymentAccount{
+		Creator: userAddr,
+	}
+	_ = s.SendTxBlock(user, msgCreatePaymentAccount)
+	paymentAccountsReq := &paymenttypes.QueryGetPaymentAccountsByOwnerRequest{Owner: userAddr}
+	paymentAccounts, err := s.Client.PaymentQueryClient.GetPaymentAccountsByOwner(ctx, paymentAccountsReq)
+	s.Require().NoError(err)
+	s.T().Logf("paymentAccounts %s", core.YamlString(paymentAccounts))
+	paymentAddr := paymentAccounts.PaymentAccounts[0]
+	s.Require().Lenf(paymentAccounts.PaymentAccounts, 1, "paymentAccounts %s", core.YamlString(paymentAccounts))
+
+	// deposit BNB needed
+	msgDeposit := &paymenttypes.MsgDeposit{
+		Creator: user.GetAddr().String(),
+		To:      paymentAddr,
+		Amount:  paymentAccountBNBNeeded,
+	}
+	_ = s.SendTxBlock(user, msgDeposit)
+
+	// create bucket
+	bucketName := storagetestutils.GenRandomBucketName()
+	msgCreateBucket := storagetypes.NewMsgCreateBucket(
+		user.GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PUBLIC_READ, sp.OperatorKey.GetAddr(),
+		sdk.MustAccAddressFromHex(paymentAddr), math.MaxUint, nil, bucketChargedReadQuota)
+	msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
+	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
+	s.Require().NoError(err)
+	s.SendTxBlock(user, msgCreateBucket)
+
+	// check payment account stream record
+	paymentAccountStreamRecord := s.GetStreamRecord(paymentAddr)
+	s.T().Logf("paymentAccountStreamRecord %s", core.YamlString(paymentAccountStreamRecord))
+	s.Require().Equal(expectedRate.String(), paymentAccountStreamRecord.NetflowRate.Neg().String())
+	s.Require().Equal(paymentAccountStreamRecord.BufferBalance.String(), paymentAccountBNBNeeded.String())
+	s.Require().Equal(paymentAccountStreamRecord.StaticBalance.String(), sdkmath.ZeroInt().String())
+
+	// wait until settle time
+	retryCount := 0
+	for {
+		latestBlock, err := s.TmClient.TmClient.Block(ctx, nil)
+		s.Require().NoError(err)
+		currentTimestamp := latestBlock.Block.Time.Unix()
+		s.T().Logf("currentTimestamp %d, paymentAccountStreamRecord.SettleTimestamp %d", currentTimestamp, paymentAccountStreamRecord.SettleTimestamp)
+		if currentTimestamp > paymentAccountStreamRecord.SettleTimestamp {
+			break
+		}
+		time.Sleep(time.Second)
+		retryCount++
+		if retryCount > 60 {
+			s.T().Fatalf("wait for settle time timeout")
+		}
+	}
+	// check auto settle
+	paymentStreamRecordAfterAutoSettle := s.GetStreamRecord(paymentAddr)
+	s.T().Logf("paymentStreamRecordAfterAutoSettle %s", core.YamlString(paymentStreamRecordAfterAutoSettle))
+	s.Require().NotEqual(paymentStreamRecordAfterAutoSettle.Status, paymenttypes.STREAM_ACCOUNT_STATUS_ACTIVE)
+
+	// deposit and resume
+	depositAmount := sdk.NewInt(1e10)
+	msgDeposit = &paymenttypes.MsgDeposit{
+		Creator: userAddr,
+		To:      paymentAddr,
+		Amount:  depositAmount,
+	}
+	mode := tx.BroadcastMode_BROADCAST_MODE_ASYNC
+	txOpt := types.TxOption{
+		Mode: &mode,
+		Memo: "",
+	}
+	s.SendTxWithTxOpt(msgDeposit, user, txOpt)
+
+	// check payment account stream record
+	paymentAccountStreamRecordAfterDeposit := s.GetStreamRecord(paymentAddr)
+	s.T().Logf("paymentAccountStreamRecordAfterDeposit %s", core.YamlString(paymentAccountStreamRecordAfterDeposit))
+	s.Require().NotEqual(paymentAccountStreamRecordAfterDeposit.Status, paymenttypes.STREAM_ACCOUNT_STATUS_ACTIVE)
+
+	// wait blocks
+	for {
+		latestBlock, err := s.TmClient.TmClient.Block(ctx, nil)
+		s.Require().NoError(err)
+
+		paymentAccountStreamRecordAfterDeposit = s.GetStreamRecord(paymentAddr)
+		s.T().Logf("paymentAccountStreamRecordAfterDeposit %s at %d", core.YamlString(paymentAccountStreamRecordAfterDeposit), latestBlock.Block.Height)
+		if paymentAccountStreamRecordAfterDeposit.Status == paymenttypes.STREAM_ACCOUNT_STATUS_ACTIVE {
+			break
+		}
+		time.Sleep(time.Second)
+		retryCount++
+		if retryCount > 60 {
+			s.T().Fatalf("wait for resume time timeout")
+		}
+	}
+
+	// revert params
+	params.MaxAutoResumeFlowCount = oldMaxAutoResumeFlowCount
+	s.updateParams(params)
+}
+
+func (s *PaymentTestSuite) TestAutoSettle_InOneBlock() {
+	ctx := context.Background()
+	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
+	user := s.GenAndChargeAccounts(1, 1000000)[0]
+	userAddr := user.GetAddr().String()
+	var err error
+
+	queryFamilyResponse, err := s.Client.GlobalVirtualGroupFamily(ctx, &virtualgroupmoduletypes.QueryGlobalVirtualGroupFamilyRequest{
+		StorageProviderId: sp.Info.Id,
+		FamilyId:          gvg.FamilyId,
+	})
+	s.Require().NoError(err)
+	family := queryFamilyResponse.GlobalVirtualGroupFamily
+
+	bucketChargedReadQuota := uint64(1000)
+	paymentParams, err := s.Client.PaymentQueryClient.Params(ctx, &paymenttypes.QueryParamsRequest{})
+	s.T().Logf("paymentParams %s, err: %v", paymentParams, err)
+	s.Require().NoError(err)
+	reserveTime := paymentParams.Params.VersionedParams.ReserveTime
+	forcedSettleTime := paymentParams.Params.ForcedSettleTime
+	queryGetSpStoragePriceByTimeResp, err := s.Client.QueryGetSpStoragePriceByTime(ctx, &sptypes.QueryGetSpStoragePriceByTimeRequest{
+		SpAddr: sp.OperatorKey.GetAddr().String(),
+	})
+	s.T().Logf("queryGetSpStoragePriceByTimeResp %s, err: %v", queryGetSpStoragePriceByTimeResp, err)
+	s.Require().NoError(err)
+	readPrice := queryGetSpStoragePriceByTimeResp.SpStoragePrice.ReadPrice
+	totalUserRate := readPrice.MulInt(sdkmath.NewIntFromUint64(bucketChargedReadQuota)).TruncateInt()
+	taxRateParam := paymentParams.Params.VersionedParams.ValidatorTaxRate
+	taxStreamRate := taxRateParam.MulInt(totalUserRate).TruncateInt()
+	expectedRate := totalUserRate.Add(taxStreamRate)
+	paymentAccountBNBNeeded := expectedRate.Mul(sdkmath.NewIntFromUint64(reserveTime))
+
+	// create payment account and deposit
+	msgCreatePaymentAccount := &paymenttypes.MsgCreatePaymentAccount{
+		Creator: userAddr,
+	}
+	_ = s.SendTxBlock(user, msgCreatePaymentAccount)
+	paymentAccountsReq := &paymenttypes.QueryGetPaymentAccountsByOwnerRequest{Owner: userAddr}
+	paymentAccounts, err := s.Client.PaymentQueryClient.GetPaymentAccountsByOwner(ctx, paymentAccountsReq)
+	s.Require().NoError(err)
+	s.T().Logf("paymentAccounts %s", core.YamlString(paymentAccounts))
+	paymentAddr := paymentAccounts.PaymentAccounts[0]
+	s.Require().Lenf(paymentAccounts.PaymentAccounts, 1, "paymentAccounts %s", core.YamlString(paymentAccounts))
+	msgDeposit := &paymenttypes.MsgDeposit{
+		Creator: user.GetAddr().String(),
+		To:      paymentAddr,
+		Amount:  paymentAccountBNBNeeded,
+	}
+	_ = s.SendTxBlock(user, msgDeposit)
+
+	// create bucket from payment account
+	bucketName := storagetestutils.GenRandomBucketName()
+	msgCreateBucket := storagetypes.NewMsgCreateBucket(
+		user.GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PUBLIC_READ, sp.OperatorKey.GetAddr(),
+		sdk.MustAccAddressFromHex(paymentAddr), math.MaxUint, nil, bucketChargedReadQuota)
+	msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
+	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
+	s.Require().NoError(err)
+	s.SendTxBlock(user, msgCreateBucket)
+	// check payment account stream record
+	paymentAccountStreamRecord := s.GetStreamRecord(paymentAddr)
+	s.T().Logf("paymentAccountStreamRecord %s", core.YamlString(paymentAccountStreamRecord))
+	s.Require().Equal(expectedRate.String(), paymentAccountStreamRecord.NetflowRate.Neg().String())
+	s.Require().Equal(paymentAccountStreamRecord.BufferBalance.String(), paymentAccountBNBNeeded.String())
+	s.Require().Equal(paymentAccountStreamRecord.StaticBalance.String(), sdkmath.ZeroInt().String())
+
+	// increase bucket charged read quota is not allowed since the balance is not enough
+	msgUpdateBucketInfo := &storagetypes.MsgUpdateBucketInfo{
+		Operator:         user.GetAddr().String(),
+		BucketName:       bucketName,
+		ChargedReadQuota: &common.UInt64Value{Value: bucketChargedReadQuota + 1},
+		Visibility:       storagetypes.VISIBILITY_TYPE_PUBLIC_READ,
+	}
+	_, err = s.SendTxBlockWithoutCheck(msgUpdateBucketInfo, user)
+	s.Require().ErrorContains(err, "balance not enough, lack of")
+
+	// create bucket from user
+	msgCreateBucket.BucketName = storagetestutils.GenRandomBucketName()
+	msgCreateBucket.PaymentAddress = ""
+	msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
+	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
+	s.Require().NoError(err)
+	s.SendTxBlock(user, msgCreateBucket)
+
+	// check user stream record
+	userStreamRecord := s.GetStreamRecord(userAddr)
+	s.T().Logf("userStreamRecord %s", core.YamlString(userStreamRecord))
+	s.Require().Equal(userStreamRecord.SettleTimestamp, userStreamRecord.CrudTimestamp+int64(reserveTime-forcedSettleTime))
+	familyStreamRecord := s.GetStreamRecord(family.VirtualPaymentAddress)
+	s.T().Logf("familyStreamRecord %s", core.YamlString(familyStreamRecord))
+	govStreamRecord := s.GetStreamRecord(paymenttypes.GovernanceAddress.String())
+	s.T().Logf("govStreamRecord %s", core.YamlString(govStreamRecord))
+
+	// wait until settle time
+	retryCount := 0
+	for {
+		latestBlock, err := s.TmClient.TmClient.Block(ctx, nil)
+		s.Require().NoError(err)
+		currentTimestamp := latestBlock.Block.Time.Unix()
+		s.T().Logf("currentTimestamp %d, userStreamRecord.SettleTimestamp %d", currentTimestamp, userStreamRecord.SettleTimestamp)
+		if currentTimestamp > userStreamRecord.SettleTimestamp {
+			break
+		}
+		time.Sleep(time.Second)
+		retryCount++
+		if retryCount > 60 {
+			s.T().Fatalf("wait for settle time timeout")
+		}
+	}
+	// check auto settle
+	userStreamRecordAfterAutoSettle := s.GetStreamRecord(userAddr)
+	s.T().Logf("userStreamRecordAfterAutoSettle %s", core.YamlString(userStreamRecordAfterAutoSettle))
+	familyStreamRecordAfterAutoSettle := s.GetStreamRecord(family.VirtualPaymentAddress)
+	s.T().Logf("familyStreamRecordAfterAutoSettle %s", core.YamlString(familyStreamRecordAfterAutoSettle))
+	paymentAccountStreamRecordAfterAutoSettle := s.GetStreamRecord(paymentAddr)
+	s.T().Logf("paymentAccountStreamRecordAfterAutoSettle %s", core.YamlString(paymentAccountStreamRecordAfterAutoSettle))
+	// payment account become frozen
+	s.Require().NotEqual(paymentAccountStreamRecordAfterAutoSettle.Status, paymenttypes.STREAM_ACCOUNT_STATUS_ACTIVE)
+	s.Require().Equal(familyStreamRecordAfterAutoSettle.Status, paymenttypes.STREAM_ACCOUNT_STATUS_ACTIVE)
+	s.Require().Equal(userStreamRecordAfterAutoSettle.Status, paymenttypes.STREAM_ACCOUNT_STATUS_ACTIVE)
+	// user settle time become refreshed
+	s.Require().NotEqual(userStreamRecordAfterAutoSettle.SettleTimestamp, userStreamRecord.SettleTimestamp)
+	s.Require().Equal(userStreamRecordAfterAutoSettle.SettleTimestamp, userStreamRecordAfterAutoSettle.CrudTimestamp+int64(reserveTime-forcedSettleTime))
+	// gov stream record balance increase
+	govStreamRecordAfterSettle := s.GetStreamRecord(paymenttypes.GovernanceAddress.String())
+	s.T().Logf("govStreamRecordAfterSettle %s", core.YamlString(govStreamRecordAfterSettle))
+	s.Require().NotEqual(govStreamRecordAfterSettle.StaticBalance.String(), govStreamRecord.StaticBalance.String())
+	govStreamRecordStaticBalanceDelta := govStreamRecordAfterSettle.StaticBalance.Sub(govStreamRecord.StaticBalance)
+	expectedGovBalanceDelta := userStreamRecord.NetflowRate.Neg().MulRaw(userStreamRecordAfterAutoSettle.CrudTimestamp - userStreamRecord.CrudTimestamp)
+	s.Require().Equal(expectedGovBalanceDelta.String(), govStreamRecordStaticBalanceDelta.String())
+
+	// deposit, balance not enough to resume
+	depositAmount1 := sdk.NewInt(1)
+	msgDeposit1 := &paymenttypes.MsgDeposit{
+		Creator: userAddr,
+		To:      paymentAddr,
+		Amount:  depositAmount1,
+	}
+	_ = s.SendTxBlock(user, msgDeposit1)
+	// check payment account stream record
+	paymentAccountStreamRecordAfterDeposit1 := s.GetStreamRecord(paymentAddr)
+	s.T().Logf("paymentAccountStreamRecordAfterDeposit1 %s", core.YamlString(paymentAccountStreamRecordAfterDeposit1))
+	s.Require().NotEqual(paymentAccountStreamRecordAfterDeposit1.Status, paymenttypes.STREAM_ACCOUNT_STATUS_ACTIVE)
+	s.Require().Equal(paymentAccountStreamRecordAfterDeposit1.StaticBalance.String(), paymentAccountStreamRecordAfterAutoSettle.StaticBalance.Add(depositAmount1).String())
+
+	// deposit and resume
+	depositAmount2 := sdk.NewInt(1e10)
+	msgDeposit2 := &paymenttypes.MsgDeposit{
+		Creator: userAddr,
+		To:      paymentAddr,
+		Amount:  depositAmount2,
+	}
+	s.SendTxBlock(user, msgDeposit2)
+	// check payment account stream record
+	paymentAccountStreamRecordAfterDeposit2 := s.GetStreamRecord(paymentAddr)
+	s.T().Logf("paymentAccountStreamRecordAfterDeposit2 %s", core.YamlString(paymentAccountStreamRecordAfterDeposit2))
+	s.Require().Equal(paymentAccountStreamRecordAfterDeposit2.Status, paymenttypes.STREAM_ACCOUNT_STATUS_ACTIVE)
+	s.Require().Equal(paymentAccountStreamRecordAfterDeposit2.StaticBalance.Add(paymentAccountStreamRecordAfterDeposit2.BufferBalance).String(), paymentAccountStreamRecordAfterDeposit1.StaticBalance.Add(depositAmount2).String())
+}
+
+func (s *PaymentTestSuite) TestAutoSettle_InBlocks() {
+	ctx := context.Background()
+	// update params
+	queryParamsRequest := paymenttypes.QueryParamsRequest{}
+	queryParamsResponse, err := s.Client.PaymentQueryClient.Params(ctx, &queryParamsRequest)
+	s.Require().NoError(err)
+	params := queryParamsResponse.GetParams()
+	oldMaxAutoSettleFlowCount := params.MaxAutoSettleFlowCount
+	s.T().Logf("params, MaxAutoSettleFlowCount: %d", oldMaxAutoSettleFlowCount)
+
+	params.MaxAutoSettleFlowCount = 2 // update to 2
+	s.updateParams(params)
+	queryParamsResponse, err = s.Client.PaymentQueryClient.Params(ctx, &queryParamsRequest)
+	s.Require().NoError(err)
+	params = queryParamsResponse.GetParams()
+	s.T().Logf("params: %s", params.String())
+
+	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
+	user := s.GenAndChargeAccounts(1, 1000000)[0]
+	userAddr := user.GetAddr().String()
+
+	reserveTime := params.VersionedParams.ReserveTime
+	queryGetSpStoragePriceByTimeResp, err := s.Client.QueryGetSpStoragePriceByTime(ctx, &sptypes.QueryGetSpStoragePriceByTimeRequest{
+		SpAddr: sp.OperatorKey.GetAddr().String(),
+	})
+	s.T().Logf("queryGetSpStoragePriceByTimeResp %s, err: %v", queryGetSpStoragePriceByTimeResp, err)
+	s.Require().NoError(err)
+
+	bucketChargedReadQuota := uint64(1000)
+	readPrice := queryGetSpStoragePriceByTimeResp.SpStoragePrice.ReadPrice
+	totalUserRate := readPrice.MulInt(sdkmath.NewIntFromUint64(bucketChargedReadQuota)).TruncateInt()
+	taxRateParam := params.VersionedParams.ValidatorTaxRate
+	taxStreamRate := taxRateParam.MulInt(totalUserRate).TruncateInt()
+	expectedRate := totalUserRate.Add(taxStreamRate)
+	paymentAccountBNBNeeded := expectedRate.Mul(sdkmath.NewIntFromUint64(reserveTime))
+
+	// create payment account and deposit
+	msgCreatePaymentAccount := &paymenttypes.MsgCreatePaymentAccount{
+		Creator: userAddr,
+	}
+	_ = s.SendTxBlock(user, msgCreatePaymentAccount)
+	paymentAccountsReq := &paymenttypes.QueryGetPaymentAccountsByOwnerRequest{Owner: userAddr}
+	paymentAccounts, err := s.Client.PaymentQueryClient.GetPaymentAccountsByOwner(ctx, paymentAccountsReq)
+	s.Require().NoError(err)
+	s.T().Logf("paymentAccounts %s", core.YamlString(paymentAccounts))
+	paymentAddr := paymentAccounts.PaymentAccounts[0]
+	s.Require().Lenf(paymentAccounts.PaymentAccounts, 1, "paymentAccounts %s", core.YamlString(paymentAccounts))
+
+	// deposit BNB needed
+	msgDeposit := &paymenttypes.MsgDeposit{
+		Creator: user.GetAddr().String(),
+		To:      paymentAddr,
+		Amount:  paymentAccountBNBNeeded,
+	}
+	_ = s.SendTxBlock(user, msgDeposit)
+
+	// create bucket
+	bucketName := storagetestutils.GenRandomBucketName()
+	msgCreateBucket := storagetypes.NewMsgCreateBucket(
+		user.GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PUBLIC_READ, sp.OperatorKey.GetAddr(),
+		sdk.MustAccAddressFromHex(paymentAddr), math.MaxUint, nil, bucketChargedReadQuota)
+	msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
+	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
+	s.Require().NoError(err)
+	s.SendTxBlock(user, msgCreateBucket)
+
+	// check payment account stream record
+	paymentAccountStreamRecord := s.GetStreamRecord(paymentAddr)
+	s.T().Logf("paymentAccountStreamRecord %s", core.YamlString(paymentAccountStreamRecord))
+	s.Require().Equal(expectedRate.String(), paymentAccountStreamRecord.NetflowRate.Neg().String())
+	s.Require().Equal(paymentAccountStreamRecord.BufferBalance.String(), paymentAccountBNBNeeded.String())
+	s.Require().Equal(paymentAccountStreamRecord.StaticBalance.String(), sdkmath.ZeroInt().String())
+
+	// wait until settle time
+	retryCount := 0
+	for {
+		latestBlock, err := s.TmClient.TmClient.Block(ctx, nil)
+		s.Require().NoError(err)
+		currentTimestamp := latestBlock.Block.Time.Unix()
+		s.T().Logf("currentTimestamp %d, paymentAccountStreamRecord.SettleTimestamp %d", currentTimestamp, paymentAccountStreamRecord.SettleTimestamp)
+		if currentTimestamp > paymentAccountStreamRecord.SettleTimestamp {
+			break
+		}
+		time.Sleep(time.Second)
+		retryCount++
+		if retryCount > 60 {
+			s.T().Fatalf("wait for settle time timeout")
+		}
+	}
+	// check auto settle
+	for {
+		paymentStreamRecordAfterAutoSettle := s.GetStreamRecord(paymentAddr)
+		s.T().Logf("paymentStreamRecordAfterAutoSettle %s", core.YamlString(paymentStreamRecordAfterAutoSettle))
+		if paymentStreamRecordAfterAutoSettle.NetflowRate.IsZero() {
+			break
+		}
+		time.Sleep(500 * time.Millisecond)
+		retryCount++
+		if retryCount > 60 {
+			s.T().Fatalf("wait for settle time timeout")
+		}
+	}
+	paymentStreamRecordAfterAutoSettle := s.GetStreamRecord(paymentAddr)
+	s.T().Logf("paymentStreamRecordAfterAutoSettle %s", core.YamlString(paymentStreamRecordAfterAutoSettle))
+	s.Require().NotEqual(paymentStreamRecordAfterAutoSettle.Status, paymenttypes.STREAM_ACCOUNT_STATUS_ACTIVE)
+
+	// revert params
+	params.MaxAutoSettleFlowCount = oldMaxAutoSettleFlowCount
+	s.updateParams(params)
+}
+
+func (s *PaymentTestSuite) TestDeleteBucketWithReadQuota() {
+	var err error
+	ctx := context.Background()
+	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
+	queryFamilyResponse, err := s.Client.GlobalVirtualGroupFamily(ctx, &virtualgroupmoduletypes.QueryGlobalVirtualGroupFamilyRequest{
+		StorageProviderId: sp.Info.Id,
+		FamilyId:          gvg.FamilyId,
+	})
+	s.Require().NoError(err)
+	family := queryFamilyResponse.GlobalVirtualGroupFamily
+	user := s.GenAndChargeAccounts(1, 1000000)[0]
+
+	streamAddresses := []string{
+		user.GetAddr().String(),
+		family.VirtualPaymentAddress,
+		gvg.VirtualPaymentAddress,
+		paymenttypes.ValidatorTaxPoolAddress.String(),
+	}
+
+	// CreateBucket
+	chargedReadQuota := uint64(100)
+	bucketName := storagetestutils.GenRandomBucketName()
+	msgCreateBucket := storagetypes.NewMsgCreateBucket(
+		user.GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PUBLIC_READ, sp.OperatorKey.GetAddr(),
+		nil, math.MaxUint, nil, chargedReadQuota)
+	msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
+	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
+	s.Require().NoError(err)
+	s.SendTxBlock(user, msgCreateBucket)
+
+	streamRecordsBeforeDelete := s.GetStreamRecords(streamAddresses)
+	s.T().Logf("streamRecordsBeforeDelete: %s", core.YamlString(streamRecordsBeforeDelete))
+	s.Require().NotEqual(streamRecordsBeforeDelete.User.NetflowRate.String(), "0")
+
+	// DeleteBucket
+	msgDeleteBucket := storagetypes.NewMsgDeleteBucket(user.GetAddr(), bucketName)
+	s.SendTxBlock(user, msgDeleteBucket)
+
+	// check the billing change
+	streamRecordsAfterDelete := s.GetStreamRecords(streamAddresses)
+	s.T().Logf("streamRecordsBeforeDelete: %s", core.YamlString(streamRecordsAfterDelete))
+	s.Require().Equal(streamRecordsAfterDelete.User.NetflowRate.String(), "0")
+}
+
+func (s *PaymentTestSuite) TestStorageSmoke() {
+	var err error
+	ctx := context.Background()
+	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
+	queryFamilyResponse, err := s.Client.GlobalVirtualGroupFamily(ctx, &virtualgroupmoduletypes.QueryGlobalVirtualGroupFamilyRequest{
+		StorageProviderId: sp.Info.Id,
+		FamilyId:          gvg.FamilyId,
+	})
+	s.Require().NoError(err)
+	family := queryFamilyResponse.GlobalVirtualGroupFamily
+	user := s.GenAndChargeAccounts(1, 1000000)[0]
+
+	streamAddresses := []string{
+		user.GetAddr().String(),
+		family.VirtualPaymentAddress,
+		gvg.VirtualPaymentAddress,
+		paymenttypes.ValidatorTaxPoolAddress.String(),
+	}
+	streamRecordsBeforeCreateBucket := s.GetStreamRecords(streamAddresses)
+	s.T().Logf("streamRecordsBeforeCreateBucket: %s", core.YamlString(streamRecordsBeforeCreateBucket))
+
+	paymentParams, err := s.Client.PaymentQueryClient.Params(ctx, &paymenttypes.QueryParamsRequest{})
+	s.T().Logf("paymentParams %s, err: %v", paymentParams, err)
+	s.Require().NoError(err)
+
+	// create bucket
+	bucketName := storagetestutils.GenRandomBucketName()
+	bucketChargedReadQuota := uint64(1000)
+	msgCreateBucket := storagetypes.NewMsgCreateBucket(
+		user.GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PRIVATE, sp.OperatorKey.GetAddr(),
+		nil, math.MaxUint, nil, 0)
+	msgCreateBucket.ChargedReadQuota = bucketChargedReadQuota
+	msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
+	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
+	s.Require().NoError(err)
+	s.SendTxBlock(user, msgCreateBucket)
+
+	// check bill after creating bucket
+	userBankAccount, err := s.Client.Balance(ctx, &banktypes.QueryBalanceRequest{
+		Address: user.GetAddr().String(),
+		Denom:   s.Config.Denom,
+	})
+	s.Require().NoError(err)
+	s.T().Logf("user bank account %s", userBankAccount)
+
+	streamRecordsAfterCreateBucket := s.GetStreamRecords(streamAddresses)
+	userStreamRecord := streamRecordsAfterCreateBucket.User
+	s.Require().Equal(userStreamRecord.StaticBalance, sdkmath.ZeroInt())
+
+	// check price and rate calculation
+	queryHeadBucketRequest := storagetypes.QueryHeadBucketRequest{
+		BucketName: bucketName,
+	}
+	queryHeadBucketResponse, err := s.Client.HeadBucket(ctx, &queryHeadBucketRequest)
+	s.Require().NoError(err)
+
+	queryGetSpStoragePriceByTimeResp, err := s.Client.QueryGetSpStoragePriceByTime(ctx, &sptypes.QueryGetSpStoragePriceByTimeRequest{
+		SpAddr:    sp.OperatorKey.GetAddr().String(),
+		Timestamp: queryHeadBucketResponse.BucketInfo.CreateAt,
+	})
+	s.T().Logf("queryGetSpStoragePriceByTimeResp %s, err: %v", queryGetSpStoragePriceByTimeResp, err)
+	s.Require().NoError(err)
+
+	readPrice := queryGetSpStoragePriceByTimeResp.SpStoragePrice.ReadPrice
+	readChargeRate := readPrice.MulInt(sdk.NewIntFromUint64(queryHeadBucketResponse.BucketInfo.ChargedReadQuota)).TruncateInt()
+	s.T().Logf("readPrice: %s, readChargeRate: %s", readPrice, readChargeRate)
+	userTaxRate := paymentParams.Params.VersionedParams.ValidatorTaxRate.MulInt(readChargeRate).TruncateInt()
+	userTotalRate := readChargeRate.Add(userTaxRate)
+	s.Require().Equal(userStreamRecord.NetflowRate.Abs(), userTotalRate)
+	expectedOutFlows := []paymenttypes.OutFlow{
+		{ToAddress: family.VirtualPaymentAddress, Rate: readChargeRate},
+		{ToAddress: paymenttypes.ValidatorTaxPoolAddress.String(), Rate: userTaxRate},
+	}
+	userOutFlowsResponse, err := s.Client.OutFlows(ctx, &paymenttypes.QueryOutFlowsRequest{Account: user.GetAddr().String()})
+	s.Require().NoError(err)
+	sort.Slice(userOutFlowsResponse.OutFlows, func(i, j int) bool {
+		return userOutFlowsResponse.OutFlows[i].ToAddress < userOutFlowsResponse.OutFlows[j].ToAddress
+	})
+	sort.Slice(expectedOutFlows, func(i, j int) bool {
+		return expectedOutFlows[i].ToAddress < expectedOutFlows[j].ToAddress
+	})
+	s.Require().Equal(expectedOutFlows, userOutFlowsResponse.OutFlows)
+
+	// CreateObject
+	objectName := storagetestutils.GenRandomObjectName()
+	// create test buffer
+	var buffer bytes.Buffer
+	// Create 1MiB content where each line contains 1024 characters.
+	for i := 0; i < 1024; i++ {
+		buffer.WriteString(fmt.Sprintf("[%05d] %s\n", i, line))
+	}
+	payloadSize := buffer.Len()
+	checksum := sdk.Keccak256(buffer.Bytes())
+	expectChecksum := [][]byte{checksum, checksum, checksum, checksum, checksum, checksum, checksum}
+	contextType := "text/event-stream"
+	msgCreateObject := storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, objectName, uint64(payloadSize), storagetypes.VISIBILITY_TYPE_PRIVATE, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil)
+	msgCreateObject.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateObject.GetApprovalBytes())
+	s.Require().NoError(err)
+	// simulate
+	res := s.SimulateTx(msgCreateObject, user)
+	s.T().Logf("res %v", res.Result)
+	// check EventFeePreview in simulation result
+	var feePreviewEventEmitted bool
+	events := res.Result.Events
+	for _, event := range events {
+		if event.Type == "greenfield.payment.EventFeePreview" {
+			s.T().Logf("event %v", event)
+			feePreviewEventEmitted = true
+		}
+	}
+	s.Require().True(feePreviewEventEmitted)
+	s.SendTxBlock(user, msgCreateObject)
+
+	// check lock balance
+	queryHeadBucketResponseAfterCreateObj, err := s.Client.HeadBucket(ctx, &queryHeadBucketRequest)
+	s.T().Logf("queryHeadBucketResponseAfterCreateObj %s, err: %v", queryHeadBucketResponseAfterCreateObj, err)
+	s.Require().NoError(err)
+	queryHeadObjectRequest := storagetypes.QueryHeadObjectRequest{
+		BucketName: bucketName,
+		ObjectName: objectName,
+	}
+	queryHeadObjectResponse, err := s.Client.HeadObject(ctx, &queryHeadObjectRequest)
+	s.T().Logf("queryHeadObjectResponse %s, err: %v", queryHeadObjectResponse, err)
+	s.Require().NoError(err)
+
+	queryGetSecondarySpStorePriceByTime, err := s.Client.QueryGetSecondarySpStorePriceByTime(ctx, &sptypes.QueryGetSecondarySpStorePriceByTimeRequest{
+		Timestamp: queryHeadBucketResponse.BucketInfo.CreateAt,
+	})
+	s.T().Logf("queryGetSecondarySpStorePriceByTime %s, err: %v", queryGetSecondarySpStorePriceByTime, err)
+	s.Require().NoError(err)
+	primaryStorePrice := queryGetSpStoragePriceByTimeResp.SpStoragePrice.StorePrice
+	secondaryStorePrice := queryGetSecondarySpStorePriceByTime.SecondarySpStorePrice.StorePrice
+	chargeSize := s.GetChargeSize(queryHeadObjectResponse.ObjectInfo.PayloadSize)
+	expectedChargeRate := primaryStorePrice.Add(secondaryStorePrice.MulInt64(6)).MulInt(sdk.NewIntFromUint64(chargeSize)).TruncateInt()
+	expectedLockedBalance := expectedChargeRate.Mul(sdkmath.NewIntFromUint64(paymentParams.Params.VersionedParams.ReserveTime))
+
+	streamRecordsAfterCreateObject := s.GetStreamRecords(streamAddresses)
+	s.T().Logf("streamRecordsAfterCreateObject %s", core.YamlString(streamRecordsAfterCreateObject))
+	userStreamAccountAfterCreateObj := streamRecordsAfterCreateObject.User
+
+	s.Require().Equal(expectedLockedBalance.String(), userStreamAccountAfterCreateObj.LockBalance.String())
+
+	// seal object
+	gvgId := gvg.Id
+	msgSealObject := storagetypes.NewMsgSealObject(sp.SealKey.GetAddr(), bucketName, objectName, gvg.Id, nil)
+	secondarySigs := make([][]byte, 0)
+	secondarySPBlsPubKeys := make([]bls.PublicKey, 0)
+	blsSignHash := storagetypes.NewSecondarySpSealObjectSignDoc(s.GetChainID(), gvgId, queryHeadObjectResponse.ObjectInfo.Id, storagetypes.GenerateHash(expectChecksum[:])).GetBlsSignHash()
+	// every secondary sp signs the checksums
+	for i := 1; i < len(s.StorageProviders); i++ {
+		sig, err := core.BlsSignAndVerify(s.StorageProviders[i], blsSignHash)
+		s.Require().NoError(err)
+		secondarySigs = append(secondarySigs, sig)
+		pk, err := bls.PublicKeyFromBytes(s.StorageProviders[i].BlsKey.PubKey().Bytes())
+		s.Require().NoError(err)
+		secondarySPBlsPubKeys = append(secondarySPBlsPubKeys, pk)
+	}
+	aggBlsSig, err := core.BlsAggregateAndVerify(secondarySPBlsPubKeys, blsSignHash, secondarySigs)
+	s.Require().NoError(err)
+	msgSealObject.SecondarySpBlsAggSignatures = aggBlsSig
+	s.T().Logf("msg %s", msgSealObject.String())
+	s.SendTxBlock(sp.SealKey, msgSealObject)
+
+	// check bill after seal
+	streamRecordsAfterSeal := s.GetStreamRecords(streamAddresses)
+	s.T().Logf("streamRecordsAfterSeal %s", core.YamlString(streamRecordsAfterSeal))
+	s.Require().Equal(sdkmath.ZeroInt(), streamRecordsAfterSeal.User.LockBalance)
+	s.CheckStreamRecordsBeforeAndAfter(streamRecordsAfterCreateObject, streamRecordsAfterSeal, readPrice, readChargeRate, primaryStorePrice, secondaryStorePrice, chargeSize, uint64(payloadSize))
+
+	// query dynamic balance
+	time.Sleep(3 * time.Second)
+	queryDynamicBalanceRequest := paymenttypes.QueryDynamicBalanceRequest{
+		Account: user.GetAddr().String(),
+	}
+	queryDynamicBalanceResponse, err := s.Client.DynamicBalance(ctx, &queryDynamicBalanceRequest)
+	s.Require().NoError(err)
+	s.T().Logf("queryDynamicBalanceResponse %s", core.YamlString(queryDynamicBalanceResponse))
+
+	// create empty object
+	streamRecordsBeforeCreateEmptyObject := s.GetStreamRecords(streamAddresses)
+	s.T().Logf("streamRecordsBeforeCreateEmptyObject %s", core.YamlString(streamRecordsBeforeCreateEmptyObject))
+
+	emptyObjectName := "sub_directory/"
+	// create empty test buffer
+	var emptyBuffer bytes.Buffer
+	emptyPayloadSize := emptyBuffer.Len()
+	emptyChecksum := sdk.Keccak256(emptyBuffer.Bytes())
+	emptyExpectChecksum := [][]byte{emptyChecksum, emptyChecksum, emptyChecksum, emptyChecksum, emptyChecksum, emptyChecksum, emptyChecksum}
+	msgCreateEmptyObject := storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, emptyObjectName, uint64(emptyPayloadSize), storagetypes.VISIBILITY_TYPE_PRIVATE, emptyExpectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil)
+	msgCreateEmptyObject.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateEmptyObject.GetApprovalBytes())
+	s.Require().NoError(err)
+	s.SendTxBlock(user, msgCreateEmptyObject)
+
+	streamRecordsAfterCreateEmptyObject := s.GetStreamRecords(streamAddresses)
+	s.T().Logf("streamRecordsAfterCreateEmptyObject %s", core.YamlString(streamRecordsAfterCreateEmptyObject))
+	chargeSize = s.GetChargeSize(uint64(emptyPayloadSize))
+	s.CheckStreamRecordsBeforeAndAfter(streamRecordsBeforeCreateEmptyObject, streamRecordsAfterCreateEmptyObject, readPrice, readChargeRate, primaryStorePrice, secondaryStorePrice, chargeSize, uint64(emptyPayloadSize))
+
+	// test query auto settle records
+	queryAllAutoSettleRecordRequest := paymenttypes.QueryAllAutoSettleRecordRequest{}
+	queryAllAutoSettleRecordResponse, err := s.Client.AutoSettleRecordAll(ctx, &queryAllAutoSettleRecordRequest)
+	s.Require().NoError(err)
+	s.T().Logf("queryAllAutoSettleRecordResponse %s", core.YamlString(queryAllAutoSettleRecordResponse))
+	s.Require().True(len(queryAllAutoSettleRecordResponse.AutoSettleRecord) >= 1)
+
+	// simulate delete object, check fee preview
+	deleteObjectMsg := storagetypes.NewMsgDeleteObject(user.GetAddr(), bucketName, objectName)
+	deleteObjectSimRes := s.SimulateTx(deleteObjectMsg, user)
+	s.T().Logf("deleteObjectSimRes %v", deleteObjectSimRes.Result)
+}
+
+// TestForceDeletion_DeleteAfterPriceChange will cover the following case:
+// create an object, sp increase the price a lot, the object can be force deleted even the object's own has no enough balance.
+func (s *PaymentTestSuite) TestForceDeletion_AfterPriceChange() {
+	ctx := context.Background()
+
+	// set storage price
+	sp := s.StorageProviders[0]
+	priceRes, err := s.Client.QueryGetSpStoragePriceByTime(ctx, &sptypes.QueryGetSpStoragePriceByTimeRequest{
+		SpAddr:    sp.OperatorKey.GetAddr().String(),
+		Timestamp: 0,
+	})
+	s.Require().NoError(err)
+	s.T().Log("price", priceRes.SpStoragePrice)
+
+	// create bucket
+	user, bucketName := s.createBucket()
+
+	// create & seal objects
+	_, _, objectName1, objectId1, checksums1 := s.createObject(user, bucketName)
+	s.sealObject(bucketName, objectName1, objectId1, checksums1)
+
+	_, _, objectName2, objectId2, checksums2 := s.createObject(user, bucketName)
+	s.sealObject(bucketName, objectName2, objectId2, checksums2)
+
+	// update new price
+	msgUpdatePrice := &sptypes.MsgUpdateSpStoragePrice{
+		SpAddress:     sp.OperatorKey.GetAddr().String(),
+		ReadPrice:     priceRes.SpStoragePrice.ReadPrice,
+		FreeReadQuota: priceRes.SpStoragePrice.FreeReadQuota,
+		StorePrice:    priceRes.SpStoragePrice.StorePrice.MulInt64(10000),
+	}
+	s.SendTxBlock(sp.OperatorKey, msgUpdatePrice)
+
+	// for payment
+	time.Sleep(2 * time.Second)
+
+	queryBalanceRequest := banktypes.QueryBalanceRequest{Denom: s.Config.Denom, Address: user.GetAddr().String()}
+	queryBalanceResponse, err := s.Client.BankQueryClient.Balance(ctx, &queryBalanceRequest)
+	s.Require().NoError(err)
+
+	msgSend := banktypes.NewMsgSend(user.GetAddr(), core.GenRandomAddr(), sdk.NewCoins(
+		sdk.NewCoin(s.Config.Denom, queryBalanceResponse.Balance.Amount.SubRaw(5*types.DecimalGwei)),
+	))
+
+	simulateResponse := s.SimulateTx(msgSend, user)
+	gasLimit := simulateResponse.GasInfo.GetGasUsed()
+	gasPrice, err := sdk.ParseCoinNormalized(simulateResponse.GasInfo.GetMinGasPrice())
+	s.Require().NoError(err)
+
+	msgSend.Amount = sdk.NewCoins(
+		sdk.NewCoin(s.Config.Denom, queryBalanceResponse.Balance.Amount.Sub(gasPrice.Amount.Mul(sdk.NewInt(int64(gasLimit))))),
+	)
+	s.SendTxBlock(user, msgSend)
+	queryBalanceResponse, err = s.Client.BankQueryClient.Balance(ctx, &queryBalanceRequest)
+	s.Require().NoError(err)
+	s.Require().Equal(int64(0), queryBalanceResponse.Balance.Amount.Int64())
+
+	queryHeadObjectRequest := storagetypes.QueryHeadObjectRequest{
+		BucketName: bucketName,
+		ObjectName: objectName1,
+	}
+	queryHeadObjectResponse, err := s.Client.HeadObject(ctx, &queryHeadObjectRequest)
+	s.Require().NoError(err)
+	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.ObjectStatus, storagetypes.OBJECT_STATUS_SEALED)
+
+	// force delete bucket
+	msgDiscontinueBucket := storagetypes.NewMsgDiscontinueBucket(sp.GcKey.GetAddr(), bucketName, "test")
+	txRes := s.SendTxBlock(sp.GcKey, msgDiscontinueBucket)
+	deleteAt := filterDiscontinueBucketEventFromTx(txRes).DeleteAt
+
+	for {
+		time.Sleep(200 * time.Millisecond)
+		statusRes, err := s.TmClient.TmClient.Status(context.Background())
+		s.Require().NoError(err)
+		blockTime := statusRes.SyncInfo.LatestBlockTime.Unix()
+
+		s.T().Logf("current blockTime: %d, delete blockTime: %d", blockTime, deleteAt)
+
+		if blockTime > deleteAt {
+			break
+		}
+	}
+
+	_, err = s.Client.HeadBucket(ctx, &storagetypes.QueryHeadBucketRequest{BucketName: bucketName})
+	s.Require().ErrorContains(err, "No such bucket")
+
+	// revert price
+	msgUpdatePrice = &sptypes.MsgUpdateSpStoragePrice{
+		SpAddress:     sp.OperatorKey.GetAddr().String(),
+		ReadPrice:     priceRes.SpStoragePrice.ReadPrice,
+		FreeReadQuota: priceRes.SpStoragePrice.FreeReadQuota,
+		StorePrice:    priceRes.SpStoragePrice.StorePrice,
+	}
+	s.SendTxBlock(sp.OperatorKey, msgUpdatePrice)
+}
+
+func (s *PaymentTestSuite) GetStreamRecord(addr string) (sr paymenttypes.StreamRecord) {
+	ctx := context.Background()
+	streamRecordResp, err := s.Client.StreamRecord(ctx, &paymenttypes.QueryGetStreamRecordRequest{
+		Account: addr,
+	})
+	if streamRecordResp != nil {
+		s.Require().NoError(err)
+		sr = streamRecordResp.StreamRecord
+	} else {
+		s.Require().ErrorContainsf(err, "not found", "account: %s", addr)
+		sr.StaticBalance = sdk.ZeroInt()
+		sr.BufferBalance = sdk.ZeroInt()
+		sr.LockBalance = sdk.ZeroInt()
+		sr.NetflowRate = sdk.ZeroInt()
+	}
+	return sr
+}
+
+func (s *PaymentTestSuite) GetStreamRecords(addrs []string) (streamRecords StreamRecords) {
+	streamRecords.User = s.GetStreamRecord(addrs[0])
+	streamRecords.GVGFamily = s.GetStreamRecord(addrs[1])
+	streamRecords.GVG = s.GetStreamRecord(addrs[2])
+	streamRecords.Tax = s.GetStreamRecord(addrs[3])
+	return
+}
+
+func (s *PaymentTestSuite) CheckStreamRecordsBeforeAndAfter(streamRecordsBefore StreamRecords, streamRecordsAfter StreamRecords, readPrice sdk.Dec,
+	readChargeRate sdkmath.Int, primaryStorePrice sdk.Dec, secondaryStorePrice sdk.Dec, chargeSize uint64, payloadSize uint64) {
+	userRateDiff := streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate)
+	gvgFamilyRateDiff := streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate)
+	gvgRateDiff := streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate)
+	taxRateDiff := streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate)
+	s.Require().Equal(userRateDiff, gvgFamilyRateDiff.Add(gvgRateDiff).Add(taxRateDiff).Neg())
+
+	outFlowsResponse, err := s.Client.OutFlows(context.Background(), &paymenttypes.QueryOutFlowsRequest{Account: streamRecordsAfter.User.Account})
+	s.Require().NoError(err)
+	userOutflowMap := lo.Reduce(outFlowsResponse.OutFlows, func(m map[string]sdkmath.Int, outflow paymenttypes.OutFlow, i int) map[string]sdkmath.Int {
+		m[outflow.ToAddress] = outflow.Rate
+		return m
+	}, make(map[string]sdkmath.Int))
+	if payloadSize != 0 {
+		gvgFamilyRate := primaryStorePrice.MulInt(sdk.NewIntFromUint64(chargeSize)).TruncateInt().Add(readChargeRate)
+		s.Require().Equal(gvgFamilyRate, userOutflowMap[streamRecordsAfter.GVGFamily.Account])
+
+		gvgRate := secondaryStorePrice.MulInt(sdk.NewIntFromUint64(chargeSize)).TruncateInt().MulRaw(6)
+		s.Require().Equal(gvgRate, userOutflowMap[streamRecordsAfter.GVG.Account])
+	}
+}
+
+func (s *PaymentTestSuite) GetChargeSize(payloadSize uint64) uint64 {
+	ctx := context.Background()
+	storageParams, err := s.Client.StorageQueryClient.Params(ctx, &storagetypes.QueryParamsRequest{})
+	s.Require().NoError(err)
+	s.T().Logf("storageParams %s", storageParams)
+	minChargeSize := storageParams.Params.VersionedParams.MinChargeSize
+	if payloadSize < minChargeSize {
+		return minChargeSize
+	} else {
+		return payloadSize
+	}
+}
+
 func TestPaymentTestSuite(t *testing.T) {
 	suite.Run(t, new(PaymentTestSuite))
 }
diff --git a/e2e/tests/permission_test.go b/e2e/tests/permission_test.go
index ef62c1f62..03470f410 100644
--- a/e2e/tests/permission_test.go
+++ b/e2e/tests/permission_test.go
@@ -24,11 +24,14 @@ func (s *StorageTestSuite) TestDeleteBucketPermission() {
 	user := s.GenAndChargeAccounts(2, 1000000)
 
 	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
 	// CreateBucket
 	bucketName := storageutil.GenRandomBucketName()
 	msgCreateBucket := storagetypes.NewMsgCreateBucket(
 		user[0].GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PUBLIC_READ, sp.OperatorKey.GetAddr(),
 		nil, math.MaxUint, nil, 0)
+	msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
 	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user[0], msgCreateBucket)
@@ -42,7 +45,7 @@ func (s *StorageTestSuite) TestDeleteBucketPermission() {
 	s.Require().NoError(err)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.BucketName, bucketName)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.Owner, user[0].GetAddr().String())
-	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PrimarySpAddress, sp.OperatorKey.GetAddr().String())
+	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PrimarySpId, sp.Info.Id)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PaymentAddress, user[0].GetAddr().String())
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.Visibility, storagetypes.VISIBILITY_TYPE_PUBLIC_READ)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.SourceType, storagetypes.SOURCE_TYPE_ORIGIN)
@@ -99,11 +102,14 @@ func (s *StorageTestSuite) TestDeletePolicy() {
 	user := s.GenAndChargeAccounts(2, 1000000)
 
 	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
 	// CreateBucket
 	bucketName := storageutil.GenRandomBucketName()
 	msgCreateBucket := storagetypes.NewMsgCreateBucket(
 		user[0].GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PUBLIC_READ, sp.OperatorKey.GetAddr(),
 		nil, math.MaxUint, nil, 0)
+	msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
 	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user[0], msgCreateBucket)
@@ -117,7 +123,7 @@ func (s *StorageTestSuite) TestDeletePolicy() {
 	s.Require().NoError(err)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.BucketName, bucketName)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.Owner, user[0].GetAddr().String())
-	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PrimarySpAddress, sp.OperatorKey.GetAddr().String())
+	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PrimarySpId, sp.Info.Id)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PaymentAddress, user[0].GetAddr().String())
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.Visibility, storagetypes.VISIBILITY_TYPE_PUBLIC_READ)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.SourceType, storagetypes.SOURCE_TYPE_ORIGIN)
@@ -199,11 +205,14 @@ func (s *StorageTestSuite) TestCreateObjectByOthers() {
 	user := s.GenAndChargeAccounts(3, 1000000)
 
 	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
 	// CreateBucket
 	bucketName := storageutil.GenRandomBucketName()
 	msgCreateBucket := storagetypes.NewMsgCreateBucket(
 		user[0].GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PUBLIC_READ, sp.OperatorKey.GetAddr(),
 		nil, math.MaxUint, nil, 0)
+	msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
 	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user[0], msgCreateBucket)
@@ -217,7 +226,7 @@ func (s *StorageTestSuite) TestCreateObjectByOthers() {
 	s.Require().NoError(err)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.BucketName, bucketName)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.Owner, user[0].GetAddr().String())
-	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PrimarySpAddress, sp.OperatorKey.GetAddr().String())
+	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PrimarySpId, sp.Info.Id)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PaymentAddress, user[0].GetAddr().String())
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.Visibility, storagetypes.VISIBILITY_TYPE_PUBLIC_READ)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.SourceType, storagetypes.SOURCE_TYPE_ORIGIN)
@@ -287,7 +296,7 @@ func (s *StorageTestSuite) TestCreateObjectByOthers() {
 	expectChecksum := [][]byte{checksum, checksum, checksum, checksum, checksum, checksum, checksum}
 	contextType := "text/event-stream"
 	msgCreateObject := storagetypes.NewMsgCreateObject(user[1].GetAddr(), bucketName, objectName, uint64(payloadSize),
-		storagetypes.VISIBILITY_TYPE_PUBLIC_READ, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil, nil)
+		storagetypes.VISIBILITY_TYPE_PUBLIC_READ, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil)
 	msgCreateObject.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateObject.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user[1], msgCreateObject)
@@ -310,7 +319,7 @@ func (s *StorageTestSuite) TestCreateObjectByOthers() {
 	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.SourceType, storagetypes.SOURCE_TYPE_ORIGIN)
 	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.RedundancyType, storagetypes.REDUNDANCY_EC_TYPE)
 	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.ContentType, contextType)
-	s.Require().Equal(len(queryHeadObjectResponse.ObjectInfo.SecondarySpAddresses), 1)
+	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.Creator, user[1].GetAddr().String())
 
 	// CancelCreateObject
 	msgCancelCreateObject := storagetypes.NewMsgCancelCreateObject(user[2].GetAddr(), bucketName, objectName)
@@ -324,7 +333,7 @@ func (s *StorageTestSuite) TestCreateObjectByOthers() {
 
 	// CreateObject
 	msgCreateObject = storagetypes.NewMsgCreateObject(user[1].GetAddr(), bucketName, objectName, uint64(payloadSize),
-		storagetypes.VISIBILITY_TYPE_PUBLIC_READ, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil, nil)
+		storagetypes.VISIBILITY_TYPE_PUBLIC_READ, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil)
 	msgCreateObject.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateObject.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user[1], msgCreateObject)
@@ -347,7 +356,7 @@ func (s *StorageTestSuite) TestCreateObjectByOthers() {
 	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.SourceType, storagetypes.SOURCE_TYPE_ORIGIN)
 	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.RedundancyType, storagetypes.REDUNDANCY_EC_TYPE)
 	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.ContentType, contextType)
-	s.Require().Equal(len(queryHeadObjectResponse.ObjectInfo.SecondarySpAddresses), 1)
+	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.Creator, user[1].GetAddr().String())
 
 	// Owner cancel
 	msgCancelCreateObject = storagetypes.NewMsgCancelCreateObject(user[0].GetAddr(), bucketName, objectName)
@@ -375,11 +384,14 @@ func (s *StorageTestSuite) TestCreateObjectByOthersExpiration() {
 	user := s.GenAndChargeAccounts(2, 1000000)
 
 	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
 	// CreateBucket
 	bucketName := storageutil.GenRandomBucketName()
 	msgCreateBucket := storagetypes.NewMsgCreateBucket(
 		user[0].GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PUBLIC_READ, sp.OperatorKey.GetAddr(),
 		nil, math.MaxUint, nil, 0)
+	msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
 	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user[0], msgCreateBucket)
@@ -393,7 +405,7 @@ func (s *StorageTestSuite) TestCreateObjectByOthersExpiration() {
 	s.Require().NoError(err)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.BucketName, bucketName)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.Owner, user[0].GetAddr().String())
-	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PrimarySpAddress, sp.OperatorKey.GetAddr().String())
+	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PrimarySpId, sp.Info.Id)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PaymentAddress, user[0].GetAddr().String())
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.Visibility, storagetypes.VISIBILITY_TYPE_PUBLIC_READ)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.SourceType, storagetypes.SOURCE_TYPE_ORIGIN)
@@ -466,7 +478,7 @@ func (s *StorageTestSuite) TestCreateObjectByOthersExpiration() {
 	checksum := sdk.Keccak256(buffer.Bytes())
 	expectChecksum := [][]byte{checksum, checksum, checksum, checksum, checksum, checksum, checksum}
 	contextType := "text/event-stream"
-	msgCreateObject := storagetypes.NewMsgCreateObject(user[1].GetAddr(), bucketName, objectName, uint64(payloadSize), storagetypes.VISIBILITY_TYPE_PUBLIC_READ, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil, nil)
+	msgCreateObject := storagetypes.NewMsgCreateObject(user[1].GetAddr(), bucketName, objectName, uint64(payloadSize), storagetypes.VISIBILITY_TYPE_PUBLIC_READ, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil)
 	msgCreateObject.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateObject.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.T().Logf("Message: %s", msgCreateObject.String())
@@ -493,11 +505,14 @@ func (s *StorageTestSuite) TestCreateObjectByOthersLimitSize() {
 	user := s.GenAndChargeAccounts(2, 1000000)
 
 	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
 	// CreateBucket
 	bucketName := storageutil.GenRandomBucketName()
 	msgCreateBucket := storagetypes.NewMsgCreateBucket(
 		user[0].GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PUBLIC_READ, sp.OperatorKey.GetAddr(),
 		nil, math.MaxUint, nil, 0)
+	msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
 	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user[0], msgCreateBucket)
@@ -511,7 +526,7 @@ func (s *StorageTestSuite) TestCreateObjectByOthersLimitSize() {
 	s.Require().NoError(err)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.BucketName, bucketName)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.Owner, user[0].GetAddr().String())
-	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PrimarySpAddress, sp.OperatorKey.GetAddr().String())
+	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PrimarySpId, sp.Info.Id)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PaymentAddress, user[0].GetAddr().String())
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.Visibility, storagetypes.VISIBILITY_TYPE_PUBLIC_READ)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.SourceType, storagetypes.SOURCE_TYPE_ORIGIN)
@@ -583,14 +598,14 @@ func (s *StorageTestSuite) TestCreateObjectByOthersLimitSize() {
 	checksum := sdk.Keccak256(buffer.Bytes())
 	expectChecksum := [][]byte{checksum, checksum, checksum, checksum, checksum, checksum, checksum}
 	contextType := "text/event-stream"
-	msgCreateObject := storagetypes.NewMsgCreateObject(user[1].GetAddr(), bucketName, objectName, uint64(payloadSize), storagetypes.VISIBILITY_TYPE_PUBLIC_READ, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil, nil)
+	msgCreateObject := storagetypes.NewMsgCreateObject(user[1].GetAddr(), bucketName, objectName, uint64(payloadSize), storagetypes.VISIBILITY_TYPE_PUBLIC_READ, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil)
 	msgCreateObject.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateObject.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.T().Logf("Message: %s", msgCreateObject.String())
 	s.SendTxBlock(user[1], msgCreateObject)
 
 	objectName2 := storageutil.GenRandomObjectName()
-	msgCreateObject = storagetypes.NewMsgCreateObject(user[1].GetAddr(), bucketName, objectName2, uint64(payloadSize), storagetypes.VISIBILITY_TYPE_PUBLIC_READ, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil, nil)
+	msgCreateObject = storagetypes.NewMsgCreateObject(user[1].GetAddr(), bucketName, objectName2, uint64(payloadSize), storagetypes.VISIBILITY_TYPE_PUBLIC_READ, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil)
 	msgCreateObject.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateObject.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.T().Logf("Message: %s", msgCreateObject.String())
@@ -617,11 +632,14 @@ func (s *StorageTestSuite) TestGrantsPermissionToGroup() {
 	user := s.GenAndChargeAccounts(2, 1000000)
 
 	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
 	// CreateBucket
 	bucketName := storageutil.GenRandomBucketName()
 	msgCreateBucket := storagetypes.NewMsgCreateBucket(
 		user[0].GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PUBLIC_READ, sp.OperatorKey.GetAddr(),
 		nil, math.MaxUint, nil, 0)
+	msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
 	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user[0], msgCreateBucket)
@@ -635,7 +653,7 @@ func (s *StorageTestSuite) TestGrantsPermissionToGroup() {
 	s.Require().NoError(err)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.BucketName, bucketName)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.Owner, user[0].GetAddr().String())
-	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PrimarySpAddress, sp.OperatorKey.GetAddr().String())
+	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PrimarySpId, sp.Info.Id)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PaymentAddress, user[0].GetAddr().String())
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.Visibility, storagetypes.VISIBILITY_TYPE_PUBLIC_READ)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.SourceType, storagetypes.SOURCE_TYPE_ORIGIN)
@@ -707,6 +725,8 @@ func (s *StorageTestSuite) TestVisibilityPermission() {
 	user := s.GenAndChargeAccounts(2, 1000000)
 
 	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
 	// CreateBucket bucket0:public bucket1:private bucket2:default
 	bucketName0 := storageutil.GenRandomBucketName()
 	bucketName1 := storageutil.GenRandomBucketName()
@@ -733,6 +753,7 @@ func (s *StorageTestSuite) TestVisibilityPermission() {
 		msgCreateBucket := storagetypes.NewMsgCreateBucket(
 			user[0].GetAddr(), bucket.BucketName, bucket.PublicType, sp.OperatorKey.GetAddr(),
 			nil, math.MaxUint, nil, 0)
+		msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
 		msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
 		s.Require().NoError(err)
 		s.SendTxBlock(user[0], msgCreateBucket)
@@ -827,7 +848,7 @@ func (s *StorageTestSuite) TestVisibilityPermission() {
 	ctx := context.Background()
 
 	for _, object := range objects {
-		msgCreateObject0 := storagetypes.NewMsgCreateObject(user[0].GetAddr(), object.BucketName, object.ObjectName, uint64(payloadSize), object.PublicType, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil, nil)
+		msgCreateObject0 := storagetypes.NewMsgCreateObject(user[0].GetAddr(), object.BucketName, object.ObjectName, uint64(payloadSize), object.PublicType, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil)
 		msgCreateObject0.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateObject0.GetApprovalBytes())
 		s.Require().NoError(err)
 		s.SendTxBlock(user[0], msgCreateObject0)
@@ -851,6 +872,8 @@ func (s *StorageTestSuite) TestEmptyPermission() {
 	user := s.GenAndChargeAccounts(2, 1000000)
 
 	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
 	ctx := context.Background()
 
 	// CreateBucket bucket0:public bucket1:private bucket2:default
@@ -883,6 +906,7 @@ func (s *StorageTestSuite) TestEmptyPermission() {
 		msgCreateBucket := storagetypes.NewMsgCreateBucket(
 			user[0].GetAddr(), bucket.BucketName, bucket.PublicType, sp.OperatorKey.GetAddr(),
 			nil, math.MaxUint, nil, 0)
+		msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
 		msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
 		s.Require().NoError(err)
 		s.SendTxBlock(user[0], msgCreateBucket)
@@ -986,7 +1010,7 @@ func (s *StorageTestSuite) TestEmptyPermission() {
 	contextType := "text/event-stream"
 
 	for _, object := range objects {
-		msgCreateObject0 := storagetypes.NewMsgCreateObject(user[0].GetAddr(), object.BucketName, object.ObjectName, uint64(payloadSize), object.PublicType, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil, nil)
+		msgCreateObject0 := storagetypes.NewMsgCreateObject(user[0].GetAddr(), object.BucketName, object.ObjectName, uint64(payloadSize), object.PublicType, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil)
 		msgCreateObject0.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateObject0.GetApprovalBytes())
 		s.Require().NoError(err)
 		s.SendTxBlock(user[0], msgCreateObject0)
@@ -1251,6 +1275,8 @@ func (s *StorageTestSuite) TestExceedEachBlockLimitGC() {
 	owner := s.GenAndChargeAccounts(1, 10000)[0]
 	user := s.GenAndChargeAccounts(1, 10000)[0]
 	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
 
 	s.Client.SetKeyManager(owner)
 
@@ -1274,6 +1300,7 @@ func (s *StorageTestSuite) TestExceedEachBlockLimitGC() {
 		msgCreateBucket := storagetypes.NewMsgCreateBucket(
 			owner.GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PUBLIC_READ, sp.OperatorKey.GetAddr(),
 			nil, math.MaxUint, nil, 0)
+		msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
 		msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
 		s.Require().NoError(err)
 		s.SendTxWithTxOpt(msgCreateBucket, owner, txOpt)
diff --git a/e2e/tests/sp_test.go b/e2e/tests/sp_test.go
index 3359c035f..839b2e482 100644
--- a/e2e/tests/sp_test.go
+++ b/e2e/tests/sp_test.go
@@ -2,21 +2,19 @@ package tests
 
 import (
 	"context"
+	"encoding/hex"
 	"sort"
-	"strconv"
 	"testing"
-	"time"
 
+	"github.com/cometbft/cometbft/crypto/tmhash"
 	sdk "github.com/cosmos/cosmos-sdk/types"
-	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
-	"github.com/cosmos/cosmos-sdk/x/authz"
-	gov "github.com/cosmos/cosmos-sdk/x/gov/types"
-	govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
 	"github.com/stretchr/testify/suite"
 
 	"github.com/bnb-chain/greenfield/e2e/core"
 	"github.com/bnb-chain/greenfield/sdk/types"
+	"github.com/bnb-chain/greenfield/testutil/sample"
 	sptypes "github.com/bnb-chain/greenfield/x/sp/types"
+	virtualgroupmoduletypes "github.com/bnb-chain/greenfield/x/virtualgroup/types"
 )
 
 type StorageProviderTestSuite struct {
@@ -30,144 +28,64 @@ func (s *StorageProviderTestSuite) SetupSuite() {
 func (s *StorageProviderTestSuite) SetupTest() {
 }
 
-func (s *StorageProviderTestSuite) NewSpAcc() *core.SPKeyManagers {
-	userAccs := s.GenAndChargeAccounts(5, 1000000)
-	operatorAcc := userAccs[0]
-	fundingAcc := userAccs[1]
-	approvalAcc := userAccs[2]
-	sealAcc := userAccs[3]
-	gcAcc := userAccs[4]
-
-	return &core.SPKeyManagers{OperatorKey: operatorAcc, SealKey: fundingAcc,
-		FundingKey: approvalAcc, ApprovalKey: sealAcc, GcKey: gcAcc}
-}
-
-func (s *StorageProviderTestSuite) NewSpAccAndGrant() *core.SPKeyManagers {
-	// 1. create new newStorageProvider
-	newSP := s.NewSpAcc()
-
-	// 2. grant deposit authorization of sp to gov module account
-	coins := sdk.NewCoin(s.Config.Denom, types.NewIntFromInt64WithDecimal(10000, types.DecimalBNB))
-	authorization := sptypes.NewDepositAuthorization(newSP.OperatorKey.GetAddr(), &coins)
-
-	govAddr := authtypes.NewModuleAddress(gov.ModuleName)
-	now := time.Now().Add(24 * time.Hour)
-	grantMsg, err := authz.NewMsgGrant(
-		newSP.FundingKey.GetAddr(), govAddr, authorization, &now)
-	s.Require().NoError(err)
-	s.SendTxBlock(newSP.FundingKey, grantMsg)
-
-	return newSP
-}
-
 func (s *StorageProviderTestSuite) TestCreateStorageProvider() {
-	ctx := context.Background()
-	validator := s.Validator.GetAddr()
-
-	// 1. create new newStorageProvider and grant
-	newSP := s.NewSpAccAndGrant()
+	// Create a New SP
+	sp := s.BaseSuite.CreateNewStorageProvider()
 
-	// 2. submit CreateStorageProvider proposal
-	govAddr := authtypes.NewModuleAddress(gov.ModuleName)
-	deposit := sdk.Coin{
-		Denom:  s.Config.Denom,
-		Amount: types.NewIntFromInt64WithDecimal(10000, types.DecimalBNB),
-	}
-	description := sptypes.Description{
-		Moniker:  "sp_test",
-		Identity: "",
-	}
-
-	endpoint := "http://127.0.0.1:9034"
-	newReadPrice := sdk.NewDec(core.RandInt64(100, 200))
-	newStorePrice := sdk.NewDec(core.RandInt64(10000, 20000))
-	msgCreateSP, _ := sptypes.NewMsgCreateStorageProvider(govAddr,
-		newSP.OperatorKey.GetAddr(), newSP.FundingKey.GetAddr(),
-		newSP.SealKey.GetAddr(),
-		newSP.ApprovalKey.GetAddr(),
-		newSP.GcKey.GetAddr(), description,
-		endpoint, deposit, newReadPrice, 10000, newStorePrice)
-	msgProposal, err := govtypesv1.NewMsgSubmitProposal(
-		[]sdk.Msg{msgCreateSP},
-		sdk.Coins{sdk.NewCoin(s.BaseSuite.Config.Denom, types.NewIntFromInt64WithDecimal(100, types.DecimalBNB))},
-		validator.String(),
-		"test", "test", "test",
-	)
+	// query sp by id
+	querySPResp, err := s.Client.StorageProvider(context.Background(), &sptypes.QueryStorageProviderRequest{
+		Id: sp.Info.Id,
+	})
 	s.Require().NoError(err)
+	s.Require().Equal(querySPResp.StorageProvider, querySPResp.StorageProvider)
 
-	txRes := s.SendTxBlock(s.Validator, msgProposal)
-	s.Require().Equal(txRes.Code, uint32(0))
-
-	// 3. query proposal and get proposal ID
-	var proposalId uint64
-	for _, event := range txRes.Logs[0].Events {
-		if event.Type == "submit_proposal" {
-			for _, attr := range event.Attributes {
-				if attr.Key == "proposal_id" {
-					proposalId, err = strconv.ParseUint(attr.Value, 10, 0)
-					s.Require().NoError(err)
-					break
-				}
-			}
-			break
-		}
+	// sp exit
+	msgSPExit := virtualgroupmoduletypes.MsgStorageProviderExit{
+		StorageProvider: sp.OperatorKey.GetAddr().String(),
 	}
-	s.Require().True(proposalId != 0)
+	s.SendTxBlock(sp.OperatorKey, &msgSPExit)
 
-	queryProposal := &govtypesv1.QueryProposalRequest{ProposalId: proposalId}
-	_, err = s.Client.GovQueryClientV1.Proposal(ctx, queryProposal)
+	// 9 query sp status
+	querySPResp2, err := s.Client.StorageProvider(context.Background(), &sptypes.QueryStorageProviderRequest{Id: sp.Info.Id})
 	s.Require().NoError(err)
+	s.Require().Equal(querySPResp2.StorageProvider.Status, sptypes.STATUS_GRACEFUL_EXITING)
 
-	// 4. submit MsgVote and wait the proposal exec
-	msgVote := govtypesv1.NewMsgVote(validator, proposalId, govtypesv1.OptionYes, "test")
-	txRes = s.SendTxBlock(s.Validator, msgVote)
-	s.Require().Equal(txRes.Code, uint32(0))
-
-	queryVoteParamsReq := govtypesv1.QueryParamsRequest{ParamsType: "voting"}
-	queryVoteParamsResp, err := s.Client.GovQueryClientV1.Params(ctx, &queryVoteParamsReq)
-	s.Require().NoError(err)
+	// 10 complete sp exit
+	msgCompleteSPExit := virtualgroupmoduletypes.MsgCompleteStorageProviderExit{
+		StorageProvider: sp.OperatorKey.GetAddr().String(),
+	}
 
-	// 5. wait a voting period and confirm that the proposal success.
-	s.T().Logf("voting period %s", *queryVoteParamsResp.Params.VotingPeriod)
-	time.Sleep(*queryVoteParamsResp.Params.VotingPeriod)
-	time.Sleep(1 * time.Second)
-	proposalRes, err := s.Client.GovQueryClientV1.Proposal(ctx, queryProposal)
-	s.Require().NoError(err)
-	s.Require().Equal(proposalRes.Proposal.Status, govtypesv1.ProposalStatus_PROPOSAL_STATUS_PASSED)
+	s.SendTxBlock(sp.OperatorKey, &msgCompleteSPExit)
 
-	// 6. query storage provider
-	querySPReq := sptypes.QueryStorageProviderRequest{
-		SpAddress: newSP.OperatorKey.GetAddr().String(),
-	}
-	querySPResp, err := s.Client.StorageProvider(ctx, &querySPReq)
-	s.Require().NoError(err)
-	s.Require().Equal(querySPResp.StorageProvider.OperatorAddress, newSP.OperatorKey.GetAddr().String())
-	s.Require().Equal(querySPResp.StorageProvider.FundingAddress, newSP.FundingKey.GetAddr().String())
-	s.Require().Equal(querySPResp.StorageProvider.SealAddress, newSP.SealKey.GetAddr().String())
-	s.Require().Equal(querySPResp.StorageProvider.ApprovalAddress, newSP.ApprovalKey.GetAddr().String())
-	s.Require().Equal(querySPResp.StorageProvider.Endpoint, endpoint)
+	// 10 query sp
+	_, err = s.Client.StorageProvider(context.Background(), &sptypes.QueryStorageProviderRequest{Id: sp.Info.Id})
+	s.Require().Error(err)
 }
 
 func (s *StorageProviderTestSuite) TestEditStorageProvider() {
 	ctx := context.Background()
 	sp := s.StorageProviders[0]
+	blsProof, _ := sp.BlsKey.Sign(tmhash.Sum(sp.BlsKey.PubKey().Bytes()))
 
 	// 1. query previous storage provider
-	querySPReq := sptypes.QueryStorageProviderRequest{
-		SpAddress: sp.OperatorKey.GetAddr().String(),
+	querySPByOperatorAddressReq := sptypes.QueryStorageProviderByOperatorAddressRequest{
+		OperatorAddress: sp.OperatorKey.GetAddr().String(),
 	}
 
-	querySPResp, err := s.Client.StorageProvider(ctx, &querySPReq)
+	querySPByOperatorAddressResp, err := s.Client.StorageProviderByOperatorAddress(ctx, &querySPByOperatorAddressReq)
 	s.Require().NoError(err)
-	prevSP := querySPResp.StorageProvider
+	prevSP := querySPByOperatorAddressResp.StorageProvider
 
 	// 2. edit storage provider
+	newBlsPubKeyBz, newBlsProofBz := sample.RandBlsPubKeyAndBlsProofBz()
+
 	newSP := &sptypes.StorageProvider{
 		OperatorAddress: prevSP.OperatorAddress,
 		FundingAddress:  prevSP.FundingAddress,
 		SealAddress:     prevSP.SealAddress,
 		ApprovalAddress: prevSP.ApprovalAddress,
 		GcAddress:       prevSP.GcAddress,
+		BlsKey:          newBlsPubKeyBz,
 		Description: sptypes.Description{
 			Moniker:  "sp_test_edit",
 			Identity: "",
@@ -175,32 +93,38 @@ func (s *StorageProviderTestSuite) TestEditStorageProvider() {
 		Endpoint:     "http://127.0.0.1:9034",
 		TotalDeposit: prevSP.TotalDeposit,
 	}
-
 	msgEditSP := sptypes.NewMsgEditStorageProvider(
 		sp.OperatorKey.GetAddr(), newSP.Endpoint, &newSP.Description,
-		sp.SealKey.GetAddr(), sp.ApprovalKey.GetAddr(), sp.GcKey.GetAddr())
+		sp.SealKey.GetAddr(), sp.ApprovalKey.GetAddr(), sp.GcKey.GetAddr(),
+		hex.EncodeToString(newBlsPubKeyBz),
+		hex.EncodeToString(newBlsProofBz),
+	)
+
 	txRes := s.SendTxBlock(sp.OperatorKey, msgEditSP)
 	s.Require().Equal(txRes.Code, uint32(0))
 
-	// 3. query modifyed storage provider
-	querySPReq = sptypes.QueryStorageProviderRequest{
-		SpAddress: sp.OperatorKey.GetAddr().String(),
+	// 3. query modified storage provider
+	querySPReq := sptypes.QueryStorageProviderRequest{
+		Id: sp.Info.Id,
 	}
 
-	querySPResp, err = s.Client.StorageProvider(ctx, &querySPReq)
+	querySPResp, err := s.Client.StorageProvider(ctx, &querySPReq)
 	s.Require().NoError(err)
+	newSP.Id = querySPResp.StorageProvider.Id
 	s.Require().Equal(querySPResp.StorageProvider, newSP)
 
 	// 4. revert storage provider info
 	msgEditSP = sptypes.NewMsgEditStorageProvider(
 		sp.OperatorKey.GetAddr(), prevSP.Endpoint, &prevSP.Description,
-		sp.SealKey.GetAddr(), sp.ApprovalKey.GetAddr(), sp.GcKey.GetAddr())
+		sp.SealKey.GetAddr(), sp.ApprovalKey.GetAddr(), sp.GcKey.GetAddr(),
+		hex.EncodeToString(prevSP.BlsKey), hex.EncodeToString(blsProof))
+
 	txRes = s.SendTxBlock(sp.OperatorKey, msgEditSP)
 	s.Require().Equal(txRes.Code, uint32(0))
 
 	// 5. query revert storage provider again
 	querySPReq = sptypes.QueryStorageProviderRequest{
-		SpAddress: sp.OperatorKey.GetAddr().String(),
+		Id: sp.Info.Id,
 	}
 
 	querySPResp, err = s.Client.StorageProvider(ctx, &querySPReq)
diff --git a/e2e/tests/storage_test.go b/e2e/tests/storage_test.go
index b3831f4a3..517509a43 100644
--- a/e2e/tests/storage_test.go
+++ b/e2e/tests/storage_test.go
@@ -5,7 +5,6 @@ import (
 	"context"
 	"fmt"
 	"math"
-	"sort"
 	"strconv"
 	"strings"
 	"testing"
@@ -15,10 +14,9 @@ import (
 	ctypes "github.com/cometbft/cometbft/rpc/core/types"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
-	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
 	govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
 	govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
-	"github.com/samber/lo"
+	"github.com/prysmaticlabs/prysm/crypto/bls"
 	"github.com/stretchr/testify/require"
 	"github.com/stretchr/testify/suite"
 
@@ -26,10 +24,8 @@ import (
 	"github.com/bnb-chain/greenfield/sdk/keys"
 	"github.com/bnb-chain/greenfield/sdk/types"
 	storageutils "github.com/bnb-chain/greenfield/testutil/storage"
-	"github.com/bnb-chain/greenfield/types/common"
-	paymenttypes "github.com/bnb-chain/greenfield/x/payment/types"
-	sptypes "github.com/bnb-chain/greenfield/x/sp/types"
 	storagetypes "github.com/bnb-chain/greenfield/x/storage/types"
+	types2 "github.com/bnb-chain/greenfield/x/virtualgroup/types"
 )
 
 type StorageTestSuite struct {
@@ -37,12 +33,6 @@ type StorageTestSuite struct {
 	User keys.KeyManager
 }
 
-type StreamRecords struct {
-	User paymenttypes.StreamRecord
-	SPs  []paymenttypes.StreamRecord
-	Tax  paymenttypes.StreamRecord
-}
-
 func (s *StorageTestSuite) SetupSuite() {
 	s.BaseSuite.SetupSuite()
 }
@@ -67,12 +57,15 @@ var (
 func (s *StorageTestSuite) TestCreateBucket() {
 	var err error
 	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
 	user := s.User
 	// CreateBucket
 	bucketName := storageutils.GenRandomBucketName()
 	msgCreateBucket := storagetypes.NewMsgCreateBucket(
 		user.GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PUBLIC_READ, sp.OperatorKey.GetAddr(),
 		nil, math.MaxUint, nil, 0)
+	msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
 	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user, msgCreateBucket)
@@ -86,7 +79,7 @@ func (s *StorageTestSuite) TestCreateBucket() {
 	s.Require().NoError(err)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.BucketName, bucketName)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.Owner, user.GetAddr().String())
-	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PrimarySpAddress, sp.OperatorKey.GetAddr().String())
+	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PrimarySpId, sp.Info.Id)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PaymentAddress, user.GetAddr().String())
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.Visibility, storagetypes.VISIBILITY_TYPE_PUBLIC_READ)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.SourceType, storagetypes.SOURCE_TYPE_ORIGIN)
@@ -112,11 +105,14 @@ func (s *StorageTestSuite) TestCreateObject() {
 	var err error
 	// CreateBucket
 	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
 	user := s.GenAndChargeAccounts(1, 1000000)[0]
 	bucketName := storageutils.GenRandomBucketName()
 	msgCreateBucket := storagetypes.NewMsgCreateBucket(
 		user.GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PRIVATE, sp.OperatorKey.GetAddr(),
 		nil, math.MaxUint, nil, 0)
+	msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
 	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user, msgCreateBucket)
@@ -130,7 +126,7 @@ func (s *StorageTestSuite) TestCreateObject() {
 	s.Require().NoError(err)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.BucketName, bucketName)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.Owner, user.GetAddr().String())
-	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PrimarySpAddress, sp.OperatorKey.GetAddr().String())
+	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PrimarySpId, sp.Info.Id)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PaymentAddress, user.GetAddr().String())
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.Visibility, storagetypes.VISIBILITY_TYPE_PRIVATE)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.SourceType, storagetypes.SOURCE_TYPE_ORIGIN)
@@ -147,7 +143,7 @@ func (s *StorageTestSuite) TestCreateObject() {
 	checksum := sdk.Keccak256(buffer.Bytes())
 	expectChecksum := [][]byte{checksum, checksum, checksum, checksum, checksum, checksum, checksum}
 	contextType := "text/event-stream"
-	msgCreateObject := storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, objectName, uint64(payloadSize), storagetypes.VISIBILITY_TYPE_PRIVATE, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil, nil)
+	msgCreateObject := storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, objectName, uint64(payloadSize), storagetypes.VISIBILITY_TYPE_PRIVATE, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil)
 	msgCreateObject.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateObject.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user, msgCreateObject)
@@ -171,21 +167,23 @@ func (s *StorageTestSuite) TestCreateObject() {
 	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.ContentType, contextType)
 
 	// SealObject
-	secondarySPs := []sdk.AccAddress{
-		sp.OperatorKey.GetAddr(), sp.OperatorKey.GetAddr(),
-		sp.OperatorKey.GetAddr(), sp.OperatorKey.GetAddr(),
-		sp.OperatorKey.GetAddr(), sp.OperatorKey.GetAddr(),
+	gvgId := gvg.Id
+	msgSealObject := storagetypes.NewMsgSealObject(sp.SealKey.GetAddr(), bucketName, objectName, gvg.Id, nil)
+	secondarySigs := make([][]byte, 0)
+	secondarySPBlsPubKeys := make([]bls.PublicKey, 0)
+	blsSignHash := storagetypes.NewSecondarySpSealObjectSignDoc(s.GetChainID(), gvgId, queryHeadObjectResponse.ObjectInfo.Id, storagetypes.GenerateHash(queryHeadObjectResponse.ObjectInfo.Checksums[:])).GetBlsSignHash()
+	// every secondary sp signs the checksums
+	for i := 1; i < len(s.StorageProviders); i++ {
+		sig, err := core.BlsSignAndVerify(s.StorageProviders[i], blsSignHash)
+		s.Require().NoError(err)
+		secondarySigs = append(secondarySigs, sig)
+		pk, err := bls.PublicKeyFromBytes(s.StorageProviders[i].BlsKey.PubKey().Bytes())
+		s.Require().NoError(err)
+		secondarySPBlsPubKeys = append(secondarySPBlsPubKeys, pk)
 	}
-	msgSealObject := storagetypes.NewMsgSealObject(sp.SealKey.GetAddr(), bucketName, objectName, secondarySPs, nil)
-	sr := storagetypes.NewSecondarySpSignDoc(sp.OperatorKey.GetAddr(), queryHeadObjectResponse.ObjectInfo.Id, checksum)
-	secondarySig, err := sp.ApprovalKey.Sign(sr.GetSignBytes())
-	s.Require().NoError(err)
-	err = storagetypes.VerifySignature(s.StorageProviders[0].ApprovalKey.GetAddr(), sdk.Keccak256(sr.GetSignBytes()),
-		secondarySig)
+	aggBlsSig, err := core.BlsAggregateAndVerify(secondarySPBlsPubKeys, blsSignHash, secondarySigs)
 	s.Require().NoError(err)
-
-	secondarySigs := [][]byte{secondarySig, secondarySig, secondarySig, secondarySig, secondarySig, secondarySig}
-	msgSealObject.SecondarySpSignatures = secondarySigs
+	msgSealObject.SecondarySpBlsAggSignatures = aggBlsSig
 	s.T().Logf("msg %s", msgSealObject.String())
 	s.SendTxBlock(sp.SealKey, msgSealObject)
 
@@ -295,11 +293,15 @@ func (s *StorageTestSuite) TestDeleteBucket() {
 	var err error
 	user := s.GenAndChargeAccounts(1, 1000000)[0]
 	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
+	s.T().Logf("Global virtual group: %s", gvg.String())
 	// 1. CreateBucket1
 	bucketName1 := storageutils.GenRandomBucketName()
 	msgCreateBucket1 := storagetypes.NewMsgCreateBucket(
 		user.GetAddr(), bucketName1, storagetypes.VISIBILITY_TYPE_PRIVATE, sp.OperatorKey.GetAddr(),
 		nil, math.MaxUint, nil, 0)
+	msgCreateBucket1.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
 	msgCreateBucket1.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket1.
 		GetApprovalBytes())
 	s.Require().NoError(err)
@@ -310,6 +312,7 @@ func (s *StorageTestSuite) TestDeleteBucket() {
 	msgCreateBucket2 := storagetypes.NewMsgCreateBucket(
 		user.GetAddr(), bucketName2, storagetypes.VISIBILITY_TYPE_PRIVATE, sp.OperatorKey.GetAddr(),
 		nil, math.MaxUint, nil, 0)
+	msgCreateBucket2.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
 	msgCreateBucket2.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket2.
 		GetApprovalBytes())
 	s.Require().NoError(err)
@@ -329,7 +332,7 @@ func (s *StorageTestSuite) TestDeleteBucket() {
 	expectChecksum := [][]byte{checksum, checksum, checksum, checksum, checksum, checksum, checksum}
 	contextType := "text/event-stream"
 	msgCreateObject := storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName1, objectName, uint64(payloadSize),
-		storagetypes.VISIBILITY_TYPE_PRIVATE, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil, nil)
+		storagetypes.VISIBILITY_TYPE_PRIVATE, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil)
 	msgCreateObject.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateObject.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user, msgCreateObject)
@@ -344,22 +347,25 @@ func (s *StorageTestSuite) TestDeleteBucket() {
 	s.Require().NoError(err)
 
 	// SealObject
-	secondarySPs := []sdk.AccAddress{
-		sp.OperatorKey.GetAddr(), sp.OperatorKey.GetAddr(),
-		sp.OperatorKey.GetAddr(), sp.OperatorKey.GetAddr(),
-		sp.OperatorKey.GetAddr(), sp.OperatorKey.GetAddr(),
-	}
-
+	gvgId := gvg.Id
 	msgSealObject := storagetypes.NewMsgSealObject(sp.SealKey.GetAddr(), bucketName1, objectName,
-		secondarySPs, nil)
-	sr := storagetypes.NewSecondarySpSignDoc(sp.OperatorKey.GetAddr(), queryHeadObjectResponse.ObjectInfo.Id, checksum)
-	secondarySig, err := sp.ApprovalKey.Sign(sr.GetSignBytes())
-	s.Require().NoError(err)
-	err = storagetypes.VerifySignature(sp.ApprovalKey.GetAddr(), sdk.Keccak256(sr.GetSignBytes()), secondarySig)
+		gvg.Id, nil)
+	secondarySigs := make([][]byte, 0)
+	secondarySPBlsPubKeys := make([]bls.PublicKey, 0)
+	blsSignHash := storagetypes.NewSecondarySpSealObjectSignDoc(s.GetChainID(), gvgId, queryHeadObjectResponse.ObjectInfo.Id, storagetypes.GenerateHash(queryHeadObjectResponse.ObjectInfo.Checksums[:])).GetBlsSignHash()
+	// every secondary sp signs the checksums
+	for i := 1; i < len(s.StorageProviders); i++ {
+		sig, err := core.BlsSignAndVerify(s.StorageProviders[i], blsSignHash)
+		s.Require().NoError(err)
+		secondarySigs = append(secondarySigs, sig)
+		pk, err := bls.PublicKeyFromBytes(s.StorageProviders[i].BlsKey.PubKey().Bytes())
+		s.Require().NoError(err)
+		secondarySPBlsPubKeys = append(secondarySPBlsPubKeys, pk)
+	}
+	aggBlsSig, err := core.BlsAggregateAndVerify(secondarySPBlsPubKeys, blsSignHash, secondarySigs)
 	s.Require().NoError(err)
+	msgSealObject.SecondarySpBlsAggSignatures = aggBlsSig
 
-	secondarySigs := [][]byte{secondarySig, secondarySig, secondarySig, secondarySig, secondarySig, secondarySig}
-	msgSealObject.SecondarySpSignatures = secondarySigs
 	s.T().Logf("msg %s", msgSealObject.String())
 	s.SendTxBlock(sp.SealKey, msgSealObject)
 
@@ -376,464 +382,18 @@ func (s *StorageTestSuite) TestDeleteBucket() {
 	s.SendTxBlock(user, msgDeleteBucket1)
 }
 
-func (s *StorageTestSuite) GetStreamRecord(addr string) (sr paymenttypes.StreamRecord) {
-	ctx := context.Background()
-	streamRecordResp, err := s.Client.StreamRecord(ctx, &paymenttypes.QueryGetStreamRecordRequest{
-		Account: addr,
-	})
-	if streamRecordResp != nil {
-		s.Require().NoError(err)
-		sr = streamRecordResp.StreamRecord
-	} else {
-		s.Require().ErrorContainsf(err, "not found", "account: %s", addr)
-		sr.StaticBalance = sdk.ZeroInt()
-		sr.BufferBalance = sdk.ZeroInt()
-		sr.LockBalance = sdk.ZeroInt()
-		sr.NetflowRate = sdk.ZeroInt()
-	}
-	return sr
-}
-
-func (s *StorageTestSuite) GetStreamRecords() (streamRecords StreamRecords) {
-	streamRecords.User = s.GetStreamRecord(s.User.GetAddr().String())
-	for _, sp := range s.StorageProviders {
-		sr := s.GetStreamRecord(sp.FundingKey.GetAddr().String())
-		streamRecords.SPs = append(streamRecords.SPs, sr)
-	}
-	streamRecords.Tax = s.GetStreamRecord(paymenttypes.ValidatorTaxPoolAddress.String())
-	return streamRecords
-}
-
-func (s *StorageTestSuite) CheckStreamRecordsBeforeAndAfter(streamRecordsBefore StreamRecords, streamRecordsAfter StreamRecords, readPrice sdk.Dec,
-	readChargeRate sdkmath.Int, primaryStorePrice sdk.Dec, secondaryStorePrice sdk.Dec, chargeSize uint64, secondarySPs []sdk.AccAddress, payloadSize uint64) {
-	userRateDiff := streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate)
-	taxRateDiff := streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate)
-	spRateDiffs := lo.Map(streamRecordsAfter.SPs, func(sp paymenttypes.StreamRecord, i int) sdkmath.Int {
-		return sp.NetflowRate.Sub(streamRecordsBefore.SPs[i].NetflowRate)
-	})
-	spRateDiffsSum := lo.Reduce(spRateDiffs, func(sum sdkmath.Int, rate sdkmath.Int, i int) sdkmath.Int {
-		return sum.Add(rate)
-	}, sdkmath.ZeroInt())
-	s.Require().Equal(userRateDiff, spRateDiffsSum.Add(taxRateDiff).Neg())
-	spRateDiffMap := lo.Reduce(spRateDiffs, func(m map[string]sdkmath.Int, rate sdkmath.Int, i int) map[string]sdkmath.Int {
-		m[streamRecordsAfter.SPs[i].Account] = rate
-		return m
-	}, make(map[string]sdkmath.Int))
-	userOutflowMap := lo.Reduce(streamRecordsAfter.User.OutFlows, func(m map[string]sdkmath.Int, outflow paymenttypes.OutFlow, i int) map[string]sdkmath.Int {
-		m[outflow.ToAddress] = outflow.Rate
-		return m
-	}, make(map[string]sdkmath.Int))
-	if payloadSize != 0 {
-		primarySpFundingAddr := s.StorageProviders[0].FundingKey.GetAddr().String()
-		s.Require().Equal(
-			userOutflowMap[primarySpFundingAddr].Sub(readChargeRate).String(),
-			spRateDiffMap[primarySpFundingAddr].String())
-		diff := spRateDiffMap[primarySpFundingAddr].Sub(primaryStorePrice.MulInt(sdk.NewIntFromUint64(chargeSize)).TruncateInt())
-		s.T().Logf("readPrice: %s, readChargeRate: %s", readPrice, readChargeRate)
-		s.T().Logf("diff %s", diff.String())
-		s.Require().Equal(diff.String(), sdkmath.ZeroInt().String())
-		s.Require().Equal(spRateDiffMap[primarySpFundingAddr].String(), primaryStorePrice.MulInt(sdk.NewIntFromUint64(chargeSize)).TruncateInt().String())
-		for i, sp := range secondarySPs {
-			secondarySpAddr := sp.String()
-			s.Require().Equal(userOutflowMap[secondarySpAddr].String(), spRateDiffMap[secondarySpAddr].String(), "sp %d", i+1)
-			s.Require().Equal(userOutflowMap[secondarySpAddr].String(), secondaryStorePrice.MulInt(sdk.NewIntFromUint64(chargeSize)).TruncateInt().String())
-		}
-	}
-
-}
-
-func (s *StorageTestSuite) TestPayment_Smoke() {
-	ctx := context.Background()
-	sp := s.StorageProviders[0]
-	user := s.User
-	var err error
-
-	streamRecordsBeforeCreateBucket := s.GetStreamRecords()
-	s.T().Logf("streamRecordsBeforeCreateBucket: %s", core.YamlString(streamRecordsBeforeCreateBucket))
-	paymentParams, err := s.Client.PaymentQueryClient.Params(ctx, &paymenttypes.QueryParamsRequest{})
-	s.T().Logf("paymentParams %s, err: %v", paymentParams, err)
-	s.Require().NoError(err)
-
-	// create bucket
-	bucketName := storageutils.GenRandomBucketName()
-	bucketChargedReadQuota := uint64(1000)
-	msgCreateBucket := storagetypes.NewMsgCreateBucket(
-		user.GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PRIVATE, sp.OperatorKey.GetAddr(),
-		nil, math.MaxUint, nil, 0)
-	msgCreateBucket.ChargedReadQuota = bucketChargedReadQuota
-	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
-	s.Require().NoError(err)
-	s.SendTxBlock(user, msgCreateBucket)
-
-	// check bill after creating bucket
-	userBankAccount, err := s.Client.Balance(ctx, &banktypes.QueryBalanceRequest{
-		Address: user.GetAddr().String(),
-		Denom:   s.Config.Denom,
-	})
-	s.Require().NoError(err)
-	s.T().Logf("user bank account %s", userBankAccount)
-	streamRecordsAfterCreateBucket := s.GetStreamRecords()
-	usr := streamRecordsAfterCreateBucket.User
-	ssr1 := streamRecordsAfterCreateBucket.SPs[0]
-	s.Require().Equal(usr.StaticBalance, sdkmath.ZeroInt())
-	s.Require().Len(usr.OutFlows, 2)
-	// check price and rate calculation
-	queryHeadBucketRequest := storagetypes.QueryHeadBucketRequest{
-		BucketName: bucketName,
-	}
-	queryHeadBucketResponse, err := s.Client.HeadBucket(ctx, &queryHeadBucketRequest)
-	s.Require().NoError(err)
-	queryGetSpStoragePriceByTimeResp, err := s.Client.QueryGetSpStoragePriceByTime(ctx, &sptypes.QueryGetSpStoragePriceByTimeRequest{
-		SpAddr:    sp.OperatorKey.GetAddr().String(),
-		Timestamp: queryHeadBucketResponse.BucketInfo.BillingInfo.PriceTime,
-	})
-	s.T().Logf("queryGetSpStoragePriceByTimeResp %s, err: %v", queryGetSpStoragePriceByTimeResp, err)
-	s.Require().NoError(err)
-	readPrice := queryGetSpStoragePriceByTimeResp.SpStoragePrice.ReadPrice
-	readChargeRate := readPrice.MulInt(sdk.NewIntFromUint64(queryHeadBucketResponse.BucketInfo.ChargedReadQuota)).TruncateInt()
-	s.T().Logf("readPrice: %s, readChargeRate: %s", readPrice, readChargeRate)
-	userTaxRate := paymentParams.Params.VersionedParams.ValidatorTaxRate.MulInt(readChargeRate).TruncateInt()
-	userTotalRate := readChargeRate.Add(userTaxRate)
-	s.Require().Equal(usr.NetflowRate.Abs(), userTotalRate)
-	expectedOutFlows := []paymenttypes.OutFlow{
-		{ToAddress: ssr1.Account, Rate: readChargeRate},
-		{ToAddress: paymenttypes.ValidatorTaxPoolAddress.String(), Rate: userTaxRate},
-	}
-	sort.Slice(usr.OutFlows, func(i, j int) bool {
-		return usr.OutFlows[i].ToAddress < usr.OutFlows[j].ToAddress
-	})
-	sort.Slice(expectedOutFlows, func(i, j int) bool {
-		return expectedOutFlows[i].ToAddress < expectedOutFlows[j].ToAddress
-	})
-	s.Require().Equal(usr.OutFlows, expectedOutFlows)
-
-	// CreateObject
-	objectName := storageutils.GenRandomObjectName()
-	// create test buffer
-	var buffer bytes.Buffer
-	// Create 1MiB content where each line contains 1024 characters.
-	for i := 0; i < 1024; i++ {
-		buffer.WriteString(fmt.Sprintf("[%05d] %s\n", i, line))
-	}
-	payloadSize := buffer.Len()
-	checksum := sdk.Keccak256(buffer.Bytes())
-	expectChecksum := [][]byte{checksum, checksum, checksum, checksum, checksum, checksum, checksum}
-	contextType := "text/event-stream"
-	msgCreateObject := storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, objectName, uint64(payloadSize), storagetypes.VISIBILITY_TYPE_PRIVATE, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil, nil)
-	msgCreateObject.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateObject.GetApprovalBytes())
-	s.Require().NoError(err)
-	// simulate
-	res := s.SimulateTx(msgCreateObject, user)
-	s.T().Logf("res %v", res.Result)
-	// check EventFeePreview in simulation result
-	var feePreviewEventEmitted bool
-	events := res.Result.Events
-	for _, event := range events {
-		if event.Type == "greenfield.payment.EventFeePreview" {
-			s.T().Logf("event %v", event)
-			feePreviewEventEmitted = true
-		}
-	}
-	s.Require().True(feePreviewEventEmitted)
-	s.SendTxBlock(user, msgCreateObject)
-
-	// check lock balance
-	queryHeadBucketResponseAfterCreateObj, err := s.Client.HeadBucket(ctx, &queryHeadBucketRequest)
-	s.T().Logf("queryHeadBucketResponseAfterCreateObj %s, err: %v", queryHeadBucketResponseAfterCreateObj, err)
-	s.Require().NoError(err)
-	queryHeadObjectRequest := storagetypes.QueryHeadObjectRequest{
-		BucketName: bucketName,
-		ObjectName: objectName,
-	}
-	queryHeadObjectResponse, err := s.Client.HeadObject(ctx, &queryHeadObjectRequest)
-	s.T().Logf("queryHeadObjectResponse %s, err: %v", queryHeadObjectResponse, err)
-	s.Require().NoError(err)
-	streamRecordsAfterCreateObject := s.GetStreamRecords()
-	s.T().Logf("streamRecordsAfterCreateObject %s", core.YamlString(streamRecordsAfterCreateObject))
-	userStreamAccountAfterCreateObj := streamRecordsAfterCreateObject.User
-	queryGetSecondarySpStorePriceByTime, err := s.Client.QueryGetSecondarySpStorePriceByTime(ctx, &sptypes.QueryGetSecondarySpStorePriceByTimeRequest{
-		Timestamp: queryHeadBucketResponse.BucketInfo.BillingInfo.PriceTime,
-	})
-	s.T().Logf("queryGetSecondarySpStorePriceByTime %s, err: %v", queryGetSecondarySpStorePriceByTime, err)
-	s.Require().NoError(err)
-	primaryStorePrice := queryGetSpStoragePriceByTimeResp.SpStoragePrice.StorePrice
-	secondaryStorePrice := queryGetSecondarySpStorePriceByTime.SecondarySpStorePrice.StorePrice
-	chargeSize := s.GetChargeSize(queryHeadObjectResponse.ObjectInfo.PayloadSize)
-	expectedChargeRate := primaryStorePrice.Add(secondaryStorePrice.MulInt64(6)).MulInt(sdk.NewIntFromUint64(chargeSize)).TruncateInt()
-	expectedLockedBalance := expectedChargeRate.Mul(sdkmath.NewIntFromUint64(paymentParams.Params.VersionedParams.ReserveTime))
-	s.Require().Equal(expectedLockedBalance.String(), userStreamAccountAfterCreateObj.LockBalance.String())
-
-	// seal object
-	secondaryStorageProviders := s.StorageProviders[1:7]
-	secondarySPs := lo.Map(secondaryStorageProviders, func(sp core.SPKeyManagers, i int) sdk.AccAddress {
-		return sp.OperatorKey.GetAddr()
-	})
-	secondarySPFundingKeys := lo.Map(secondaryStorageProviders, func(sp core.SPKeyManagers, i int) sdk.AccAddress {
-		return sp.FundingKey.GetAddr()
-	})
-	msgSealObject := storagetypes.NewMsgSealObject(sp.SealKey.GetAddr(), bucketName, objectName, secondarySPs, nil)
-	secondarySigs := lo.Map(secondaryStorageProviders, func(sp core.SPKeyManagers, i int) []byte {
-		sr := storagetypes.NewSecondarySpSignDoc(sp.OperatorKey.GetAddr(), queryHeadObjectResponse.ObjectInfo.Id, checksum)
-		secondarySig, err := sp.ApprovalKey.Sign(sr.GetSignBytes())
-		s.Require().NoError(err)
-		err = storagetypes.VerifySignature(sp.ApprovalKey.GetAddr(), sdk.Keccak256(sr.GetSignBytes()), secondarySig)
-		s.Require().NoError(err)
-		return secondarySig
-	})
-	msgSealObject.SecondarySpSignatures = secondarySigs
-	s.T().Logf("msg %s", msgSealObject.String())
-	s.SendTxBlock(sp.SealKey, msgSealObject)
-
-	// check bill after seal
-	streamRecordsAfterSeal := s.GetStreamRecords()
-	s.T().Logf("streamRecordsAfterSeal %s", core.YamlString(streamRecordsAfterSeal))
-	s.Require().Equal(sdkmath.ZeroInt(), streamRecordsAfterSeal.User.LockBalance)
-	s.CheckStreamRecordsBeforeAndAfter(streamRecordsAfterCreateObject, streamRecordsAfterSeal, readPrice, readChargeRate, primaryStorePrice, secondaryStorePrice, chargeSize, secondarySPFundingKeys, uint64(payloadSize))
-
-	// query dynamic balance
-	time.Sleep(3 * time.Second)
-	queryDynamicBalanceRequest := paymenttypes.QueryDynamicBalanceRequest{
-		Account: user.GetAddr().String(),
-	}
-	queryDynamicBalanceResponse, err := s.Client.DynamicBalance(ctx, &queryDynamicBalanceRequest)
-	s.Require().NoError(err)
-	s.T().Logf("queryDynamicBalanceResponse %s", core.YamlString(queryDynamicBalanceResponse))
-
-	// create empty object
-	streamRecordsBeforeCreateEmptyObject := s.GetStreamRecords()
-	s.T().Logf("streamRecordsBeforeCreateEmptyObject %s", core.YamlString(streamRecordsBeforeCreateEmptyObject))
-
-	emptyObjectName := "sub_directory/"
-	// create empty test buffer
-	var emptyBuffer bytes.Buffer
-	emptyPayloadSize := emptyBuffer.Len()
-	emptyChecksum := sdk.Keccak256(emptyBuffer.Bytes())
-	emptyExpectChecksum := [][]byte{emptyChecksum, emptyChecksum, emptyChecksum, emptyChecksum, emptyChecksum, emptyChecksum, emptyChecksum}
-	msgCreateEmptyObject := storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, emptyObjectName, uint64(emptyPayloadSize), storagetypes.VISIBILITY_TYPE_PRIVATE, emptyExpectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil, nil)
-	msgCreateEmptyObject.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateEmptyObject.GetApprovalBytes())
-	s.Require().NoError(err)
-	s.SendTxBlock(user, msgCreateEmptyObject)
-
-	streamRecordsAfterCreateEmptyObject := s.GetStreamRecords()
-	s.T().Logf("streamRecordsAfterCreateEmptyObject %s", core.YamlString(streamRecordsAfterCreateEmptyObject))
-	chargeSize = s.GetChargeSize(uint64(emptyPayloadSize))
-
-	s.CheckStreamRecordsBeforeAndAfter(streamRecordsBeforeCreateEmptyObject, streamRecordsAfterCreateEmptyObject, readPrice, readChargeRate, primaryStorePrice, secondaryStorePrice, chargeSize, secondarySPs, uint64(emptyPayloadSize))
-
-	// test query auto settle records
-	queryAllAutoSettleRecordRequest := paymenttypes.QueryAllAutoSettleRecordRequest{}
-	queryAllAutoSettleRecordResponse, err := s.Client.AutoSettleRecordAll(ctx, &queryAllAutoSettleRecordRequest)
-	s.Require().NoError(err)
-	s.T().Logf("queryAllAutoSettleRecordResponse %s", core.YamlString(queryAllAutoSettleRecordResponse))
-	s.Require().True(len(queryAllAutoSettleRecordResponse.AutoSettleRecord) >= 1)
-
-	// simulate delete object, check fee preview
-	deleteObjectMsg := storagetypes.NewMsgDeleteObject(user.GetAddr(), bucketName, objectName)
-	deleteObjectSimRes := s.SimulateTx(deleteObjectMsg, user)
-	s.T().Logf("deleteObjectSimRes %v", deleteObjectSimRes.Result)
-}
-
-func (s *StorageTestSuite) TestPayment_DeleteBucketWithReadQuota() {
-	var err error
-	sp := s.StorageProviders[0]
-	user := s.User
-	// CreateBucket
-	chargedReadQuota := uint64(100)
-	bucketName := storageutils.GenRandomBucketName()
-	msgCreateBucket := storagetypes.NewMsgCreateBucket(
-		user.GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PUBLIC_READ, sp.OperatorKey.GetAddr(),
-		nil, math.MaxUint, nil, chargedReadQuota)
-	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
-	s.Require().NoError(err)
-	s.SendTxBlock(user, msgCreateBucket)
-
-	streamRecordsBeforeDelete := s.GetStreamRecords()
-	s.T().Logf("streamRecordsBeforeDelete: %s", core.YamlString(streamRecordsBeforeDelete))
-	s.Require().NotEqual(streamRecordsBeforeDelete.User.NetflowRate.String(), "0")
-
-	// DeleteBucket
-	msgDeleteBucket := storagetypes.NewMsgDeleteBucket(user.GetAddr(), bucketName)
-	s.SendTxBlock(user, msgDeleteBucket)
-
-	// check the billing change
-	streamRecordsAfterDelete := s.GetStreamRecords()
-	s.T().Logf("streamRecordsBeforeDelete: %s", core.YamlString(streamRecordsAfterDelete))
-	s.Require().Equal(streamRecordsAfterDelete.User.NetflowRate.String(), "0")
-}
-
-func (s *StorageTestSuite) TestPayment_AutoSettle() {
-	ctx := context.Background()
-	sp := s.StorageProviders[0]
-	user := s.User
-	userAddr := user.GetAddr().String()
-	var err error
-
-	bucketChargedReadQuota := uint64(1000)
-	paymentParams, err := s.Client.PaymentQueryClient.Params(ctx, &paymenttypes.QueryParamsRequest{})
-	s.T().Logf("paymentParams %s, err: %v", paymentParams, err)
-	s.Require().NoError(err)
-	reserveTime := paymentParams.Params.VersionedParams.ReserveTime
-	forcedSettleTime := paymentParams.Params.ForcedSettleTime
-	queryGetSpStoragePriceByTimeResp, err := s.Client.QueryGetSpStoragePriceByTime(ctx, &sptypes.QueryGetSpStoragePriceByTimeRequest{
-		SpAddr: sp.OperatorKey.GetAddr().String(),
-	})
-	s.T().Logf("queryGetSpStoragePriceByTimeResp %s, err: %v", queryGetSpStoragePriceByTimeResp, err)
-	s.Require().NoError(err)
-	readPrice := queryGetSpStoragePriceByTimeResp.SpStoragePrice.ReadPrice
-	totalUserRate := readPrice.MulInt(sdkmath.NewIntFromUint64(bucketChargedReadQuota)).TruncateInt()
-	taxRateParam := paymentParams.Params.VersionedParams.ValidatorTaxRate
-	taxStreamRate := taxRateParam.MulInt(totalUserRate).TruncateInt()
-	expectedRate := totalUserRate.Add(taxStreamRate)
-	paymentAccountBNBNeeded := expectedRate.Mul(sdkmath.NewIntFromUint64(reserveTime))
-
-	// create payment account and deposit
-	msgCreatePaymentAccount := &paymenttypes.MsgCreatePaymentAccount{
-		Creator: userAddr,
-	}
-	_ = s.SendTxBlock(user, msgCreatePaymentAccount)
-	paymentAccountsReq := &paymenttypes.QueryGetPaymentAccountsByOwnerRequest{Owner: userAddr}
-	paymentAccounts, err := s.Client.PaymentQueryClient.GetPaymentAccountsByOwner(ctx, paymentAccountsReq)
-	s.Require().NoError(err)
-	s.T().Logf("paymentAccounts %s", core.YamlString(paymentAccounts))
-	paymentAddr := paymentAccounts.PaymentAccounts[0]
-	s.Require().Lenf(paymentAccounts.PaymentAccounts, 1, "paymentAccounts %s", core.YamlString(paymentAccounts))
-	msgDeposit := &paymenttypes.MsgDeposit{
-		Creator: user.GetAddr().String(),
-		To:      paymentAddr,
-		Amount:  paymentAccountBNBNeeded,
-	}
-	_ = s.SendTxBlock(user, msgDeposit)
-
-	// create bucket from payment account
-	bucketName := storageutils.GenRandomBucketName()
-	msgCreateBucket := storagetypes.NewMsgCreateBucket(
-		user.GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PUBLIC_READ, sp.OperatorKey.GetAddr(),
-		sdk.MustAccAddressFromHex(paymentAddr), math.MaxUint, nil, bucketChargedReadQuota)
-	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
-	s.Require().NoError(err)
-	s.SendTxBlock(user, msgCreateBucket)
-	// check payment account stream record
-	paymentAccountStreamRecord := s.GetStreamRecord(paymentAddr)
-	s.T().Logf("paymentAccountStreamRecord %s", core.YamlString(paymentAccountStreamRecord))
-	s.Require().Equal(expectedRate.String(), paymentAccountStreamRecord.NetflowRate.Neg().String())
-	s.Require().Equal(paymentAccountStreamRecord.BufferBalance.String(), paymentAccountBNBNeeded.String())
-	s.Require().Equal(paymentAccountStreamRecord.StaticBalance.String(), sdkmath.ZeroInt().String())
-
-	// increase bucket charged read quota is not allowed since the balance is not enough
-	msgUpdateBucketInfo := &storagetypes.MsgUpdateBucketInfo{
-		Operator:         user.GetAddr().String(),
-		BucketName:       bucketName,
-		ChargedReadQuota: &common.UInt64Value{Value: bucketChargedReadQuota + 1},
-		Visibility:       storagetypes.VISIBILITY_TYPE_PUBLIC_READ,
-	}
-	_, err = s.SendTxBlockWithoutCheck(msgUpdateBucketInfo, user)
-	s.Require().ErrorContains(err, "balance not enough, lack of")
-
-	// create bucket from user
-	msgCreateBucket.BucketName = storageutils.GenRandomBucketName()
-	msgCreateBucket.PaymentAddress = ""
-	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
-	s.Require().NoError(err)
-	res := s.SendTxBlock(user, msgCreateBucket)
-	s.T().Logf("res %s", res.String())
-	// check user stream record
-	userStreamRecord := s.GetStreamRecord(userAddr)
-	s.T().Logf("userStreamRecord %s", core.YamlString(userStreamRecord))
-	s.Require().Equal(userStreamRecord.SettleTimestamp, userStreamRecord.CrudTimestamp+int64(reserveTime-forcedSettleTime))
-	spStreamRecord := s.GetStreamRecord(sp.OperatorKey.GetAddr().String())
-	s.T().Logf("spStreamRecord %s", core.YamlString(spStreamRecord))
-	govStreamRecord := s.GetStreamRecord(paymenttypes.GovernanceAddress.String())
-	s.T().Logf("govStreamRecord %s", core.YamlString(govStreamRecord))
-
-	// wait until settle time
-	retryCount := 0
-	for {
-		latestBlock, err := s.TmClient.TmClient.Block(ctx, nil)
-		s.Require().NoError(err)
-		currentTimestamp := latestBlock.Block.Time.Unix()
-		s.T().Logf("currentTimestamp %d, userStreamRecord.SettleTimestamp %d", currentTimestamp, userStreamRecord.SettleTimestamp)
-		if currentTimestamp > userStreamRecord.SettleTimestamp {
-			break
-		}
-		time.Sleep(time.Second)
-		retryCount++
-		if retryCount > 60 {
-			s.T().Fatalf("wait for settle time timeout")
-		}
-	}
-	// check auto settle
-	userStreamRecordAfterAutoSettle := s.GetStreamRecord(userAddr)
-	s.T().Logf("userStreamRecordAfterAutoSettle %s", core.YamlString(userStreamRecordAfterAutoSettle))
-	spStreamRecordAfterAutoSettle := s.GetStreamRecord(sp.OperatorKey.GetAddr().String())
-	s.T().Logf("spStreamRecordAfterAutoSettle %s", core.YamlString(spStreamRecordAfterAutoSettle))
-	paymentAccountStreamRecordAfterAutoSettle := s.GetStreamRecord(paymentAddr)
-	s.T().Logf("paymentAccountStreamRecordAfterAutoSettle %s", core.YamlString(paymentAccountStreamRecordAfterAutoSettle))
-	// payment account become frozen
-	s.Require().NotEqual(paymentAccountStreamRecordAfterAutoSettle.Status, paymenttypes.STREAM_ACCOUNT_STATUS_ACTIVE)
-	s.Require().Equal(spStreamRecordAfterAutoSettle.Status, paymenttypes.STREAM_ACCOUNT_STATUS_ACTIVE)
-	s.Require().Equal(userStreamRecordAfterAutoSettle.Status, paymenttypes.STREAM_ACCOUNT_STATUS_ACTIVE)
-	// user settle time become refreshed
-	s.Require().NotEqual(userStreamRecordAfterAutoSettle.SettleTimestamp, userStreamRecord.SettleTimestamp)
-	s.Require().Equal(userStreamRecordAfterAutoSettle.SettleTimestamp, userStreamRecordAfterAutoSettle.CrudTimestamp+int64(reserveTime-forcedSettleTime))
-	// gov stream record balance increase
-	govStreamRecordAfterSettle := s.GetStreamRecord(paymenttypes.GovernanceAddress.String())
-	s.T().Logf("govStreamRecordAfterSettle %s", core.YamlString(govStreamRecordAfterSettle))
-	s.Require().NotEqual(govStreamRecordAfterSettle.StaticBalance.String(), govStreamRecord.StaticBalance.String())
-	govStreamRecordStaticBalanceDelta := govStreamRecordAfterSettle.StaticBalance.Sub(govStreamRecord.StaticBalance)
-	expectedGovBalanceDelta := userStreamRecord.NetflowRate.Neg().MulRaw(userStreamRecordAfterAutoSettle.CrudTimestamp - userStreamRecord.CrudTimestamp)
-	s.Require().Equal(expectedGovBalanceDelta.String(), govStreamRecordStaticBalanceDelta.String())
-
-	// deposit, balance not enough to resume
-	depositAmount1 := sdk.NewInt(1)
-	msgDeposit1 := &paymenttypes.MsgDeposit{
-		Creator: userAddr,
-		To:      paymentAddr,
-		Amount:  depositAmount1,
-	}
-	_ = s.SendTxBlock(user, msgDeposit1)
-	// check payment account stream record
-	paymentAccountStreamRecordAfterDeposit1 := s.GetStreamRecord(paymentAddr)
-	s.T().Logf("paymentAccountStreamRecordAfterDeposit1 %s", core.YamlString(paymentAccountStreamRecordAfterDeposit1))
-	s.Require().NotEqual(paymentAccountStreamRecordAfterDeposit1.Status, paymenttypes.STREAM_ACCOUNT_STATUS_ACTIVE)
-	s.Require().Equal(paymentAccountStreamRecordAfterDeposit1.StaticBalance.String(), paymentAccountStreamRecordAfterAutoSettle.StaticBalance.Add(depositAmount1).String())
-
-	// deposit and resume
-	depositAmount2 := sdk.NewInt(1e10)
-	msgDeposit2 := &paymenttypes.MsgDeposit{
-		Creator: userAddr,
-		To:      paymentAddr,
-		Amount:  depositAmount2,
-	}
-	s.SendTxBlock(user, msgDeposit2)
-	// check payment account stream record
-	paymentAccountStreamRecordAfterDeposit2 := s.GetStreamRecord(paymentAddr)
-	s.T().Logf("paymentAccountStreamRecordAfterDeposit2 %s", core.YamlString(paymentAccountStreamRecordAfterDeposit2))
-	s.Require().Equal(paymentAccountStreamRecordAfterDeposit2.Status, paymenttypes.STREAM_ACCOUNT_STATUS_ACTIVE)
-	s.Require().Equal(paymentAccountStreamRecordAfterDeposit2.StaticBalance.Add(paymentAccountStreamRecordAfterDeposit2.BufferBalance).String(), paymentAccountStreamRecordAfterDeposit1.StaticBalance.Add(depositAmount2).String())
-}
-
-func (s *StorageTestSuite) GetChargeSize(payloadSize uint64) uint64 {
-	ctx := context.Background()
-	storageParams, err := s.Client.StorageQueryClient.Params(ctx, &storagetypes.QueryParamsRequest{})
-	s.Require().NoError(err)
-	s.T().Logf("storageParams %s", storageParams)
-	minChargeSize := storageParams.Params.VersionedParams.MinChargeSize
-	if payloadSize < minChargeSize {
-		return minChargeSize
-	} else {
-		return payloadSize
-	}
-}
-
 func (s *StorageTestSuite) TestMirrorBucket() {
 	var err error
 	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
 	user := s.User
 	// CreateBucket
 	bucketName := storageutils.GenRandomBucketName()
 	msgCreateBucket := storagetypes.NewMsgCreateBucket(
 		user.GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PRIVATE, sp.OperatorKey.GetAddr(),
 		nil, math.MaxUint, nil, 0)
+	msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
 	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user, msgCreateBucket)
@@ -847,7 +407,7 @@ func (s *StorageTestSuite) TestMirrorBucket() {
 	s.Require().NoError(err)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.BucketName, bucketName)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.Owner, user.GetAddr().String())
-	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PrimarySpAddress, sp.OperatorKey.GetAddr().String())
+	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PrimarySpId, sp.Info.Id)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PaymentAddress, user.GetAddr().String())
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.Visibility, storagetypes.VISIBILITY_TYPE_PRIVATE)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.SourceType, storagetypes.SOURCE_TYPE_ORIGIN)
@@ -861,6 +421,7 @@ func (s *StorageTestSuite) TestMirrorBucket() {
 	msgCreateBucket = storagetypes.NewMsgCreateBucket(
 		user.GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PRIVATE, sp.OperatorKey.GetAddr(),
 		nil, math.MaxUint, nil, 0)
+	msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
 	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user, msgCreateBucket)
@@ -874,11 +435,14 @@ func (s *StorageTestSuite) TestMirrorObject() {
 	var err error
 	// CreateBucket
 	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
 	user := s.GenAndChargeAccounts(1, 1000000)[0]
 	bucketName := storageutils.GenRandomBucketName()
 	msgCreateBucket := storagetypes.NewMsgCreateBucket(
 		user.GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PRIVATE, sp.OperatorKey.GetAddr(),
 		nil, math.MaxUint, nil, 0)
+	msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
 	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user, msgCreateBucket)
@@ -892,7 +456,7 @@ func (s *StorageTestSuite) TestMirrorObject() {
 	s.Require().NoError(err)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.BucketName, bucketName)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.Owner, user.GetAddr().String())
-	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PrimarySpAddress, sp.OperatorKey.GetAddr().String())
+	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PrimarySpId, sp.Info.Id)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PaymentAddress, user.GetAddr().String())
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.Visibility, storagetypes.VISIBILITY_TYPE_PRIVATE)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.SourceType, storagetypes.SOURCE_TYPE_ORIGIN)
@@ -909,7 +473,7 @@ func (s *StorageTestSuite) TestMirrorObject() {
 	checksum := sdk.Keccak256(buffer.Bytes())
 	expectChecksum := [][]byte{checksum, checksum, checksum, checksum, checksum, checksum, checksum}
 	contextType := "text/event-stream"
-	msgCreateObject := storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, objectName, uint64(payloadSize), storagetypes.VISIBILITY_TYPE_PRIVATE, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil, nil)
+	msgCreateObject := storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, objectName, uint64(payloadSize), storagetypes.VISIBILITY_TYPE_PRIVATE, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil)
 	msgCreateObject.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateObject.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user, msgCreateObject)
@@ -933,21 +497,22 @@ func (s *StorageTestSuite) TestMirrorObject() {
 	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.ContentType, contextType)
 
 	// SealObject
-	secondarySPs := []sdk.AccAddress{
-		sp.OperatorKey.GetAddr(), sp.OperatorKey.GetAddr(),
-		sp.OperatorKey.GetAddr(), sp.OperatorKey.GetAddr(),
-		sp.OperatorKey.GetAddr(), sp.OperatorKey.GetAddr(),
+	msgSealObject := storagetypes.NewMsgSealObject(sp.SealKey.GetAddr(), bucketName, objectName, gvg.Id, nil)
+	secondarySigs := make([][]byte, 0)
+	secondarySPBlsPubKeys := make([]bls.PublicKey, 0)
+	blsSignHash := storagetypes.NewSecondarySpSealObjectSignDoc(s.GetChainID(), gvg.Id, queryHeadObjectResponse.ObjectInfo.Id, storagetypes.GenerateHash(queryHeadObjectResponse.ObjectInfo.Checksums[:])).GetBlsSignHash()
+	// every secondary sp signs the checksums
+	for i := 1; i < len(s.StorageProviders); i++ {
+		sig, err := core.BlsSignAndVerify(s.StorageProviders[i], blsSignHash)
+		s.Require().NoError(err)
+		secondarySigs = append(secondarySigs, sig)
+		pk, err := bls.PublicKeyFromBytes(s.StorageProviders[i].BlsKey.PubKey().Bytes())
+		s.Require().NoError(err)
+		secondarySPBlsPubKeys = append(secondarySPBlsPubKeys, pk)
 	}
-	msgSealObject := storagetypes.NewMsgSealObject(sp.SealKey.GetAddr(), bucketName, objectName, secondarySPs, nil)
-	sr := storagetypes.NewSecondarySpSignDoc(sp.OperatorKey.GetAddr(), queryHeadObjectResponse.ObjectInfo.Id, checksum)
-	secondarySig, err := sp.ApprovalKey.Sign(sr.GetSignBytes())
-	s.Require().NoError(err)
-	err = storagetypes.VerifySignature(s.StorageProviders[0].ApprovalKey.GetAddr(), sdk.Keccak256(sr.GetSignBytes()),
-		secondarySig)
+	aggBlsSig, err := core.BlsAggregateAndVerify(secondarySPBlsPubKeys, blsSignHash, secondarySigs)
 	s.Require().NoError(err)
-
-	secondarySigs := [][]byte{secondarySig, secondarySig, secondarySig, secondarySig, secondarySig, secondarySig}
-	msgSealObject.SecondarySpSignatures = secondarySigs
+	msgSealObject.SecondarySpBlsAggSignatures = aggBlsSig
 	s.T().Logf("msg %s", msgSealObject.String())
 	s.SendTxBlock(sp.SealKey, msgSealObject)
 
@@ -972,7 +537,7 @@ func (s *StorageTestSuite) TestMirrorObject() {
 
 	// CreateObject
 	objectName = storageutils.GenRandomObjectName()
-	msgCreateObject = storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, objectName, uint64(payloadSize), storagetypes.VISIBILITY_TYPE_PRIVATE, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil, nil)
+	msgCreateObject = storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, objectName, uint64(payloadSize), storagetypes.VISIBILITY_TYPE_PRIVATE, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil)
 	msgCreateObject.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateObject.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user, msgCreateObject)
@@ -985,16 +550,23 @@ func (s *StorageTestSuite) TestMirrorObject() {
 	s.Require().NoError(err)
 
 	// SealObject
-	msgSealObject = storagetypes.NewMsgSealObject(sp.SealKey.GetAddr(), bucketName, objectName, secondarySPs, nil)
-	sr = storagetypes.NewSecondarySpSignDoc(sp.OperatorKey.GetAddr(), queryHeadObjectResponse.ObjectInfo.Id, checksum)
-	secondarySig, err = sp.ApprovalKey.Sign(sr.GetSignBytes())
-	s.Require().NoError(err)
-	err = storagetypes.VerifySignature(s.StorageProviders[0].ApprovalKey.GetAddr(), sdk.Keccak256(sr.GetSignBytes()),
-		secondarySig)
+	gvgId := gvg.Id
+	msgSealObject = storagetypes.NewMsgSealObject(sp.SealKey.GetAddr(), bucketName, objectName, gvgId, nil)
+	secondarySigs = make([][]byte, 0)
+	secondarySPBlsPubKeys = make([]bls.PublicKey, 0)
+	blsSignHash = storagetypes.NewSecondarySpSealObjectSignDoc(s.GetChainID(), gvgId, queryHeadObjectResponse.ObjectInfo.Id, storagetypes.GenerateHash(queryHeadObjectResponse.ObjectInfo.Checksums[:])).GetBlsSignHash()
+	// every secondary sp signs the checksums
+	for i := 1; i < len(s.StorageProviders); i++ {
+		sig, err := core.BlsSignAndVerify(s.StorageProviders[i], blsSignHash)
+		s.Require().NoError(err)
+		secondarySigs = append(secondarySigs, sig)
+		pk, err := bls.PublicKeyFromBytes(s.StorageProviders[i].BlsKey.PubKey().Bytes())
+		s.Require().NoError(err)
+		secondarySPBlsPubKeys = append(secondarySPBlsPubKeys, pk)
+	}
+	aggBlsSig, err = core.BlsAggregateAndVerify(secondarySPBlsPubKeys, blsSignHash, secondarySigs)
 	s.Require().NoError(err)
-
-	secondarySigs = [][]byte{secondarySig, secondarySig, secondarySig, secondarySig, secondarySig, secondarySig}
-	msgSealObject.SecondarySpSignatures = secondarySigs
+	msgSealObject.SecondarySpBlsAggSignatures = aggBlsSig
 	s.SendTxBlock(sp.SealKey, msgSealObject)
 
 	// MirrorObject using names
@@ -1331,19 +903,22 @@ func (s *StorageTestSuite) TestDiscontinueBucket_UserDeleted() {
 }
 
 // createObject with default VISIBILITY_TYPE_PRIVATE
-func (s *StorageTestSuite) createObject() (core.SPKeyManagers, keys.KeyManager, string, storagetypes.Uint, string, storagetypes.Uint) {
+func (s *StorageTestSuite) createObject() (core.StorageProvider, keys.KeyManager, string, storagetypes.Uint, string, storagetypes.Uint) {
 	return s.createObjectWithVisibility(storagetypes.VISIBILITY_TYPE_PRIVATE)
 }
 
-func (s *StorageTestSuite) createObjectWithVisibility(v storagetypes.VisibilityType) (core.SPKeyManagers, keys.KeyManager, string, storagetypes.Uint, string, storagetypes.Uint) {
+func (s *StorageTestSuite) createObjectWithVisibility(v storagetypes.VisibilityType) (core.StorageProvider, keys.KeyManager, string, storagetypes.Uint, string, storagetypes.Uint) {
 	var err error
 	// CreateBucket
 	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
 	user := s.GenAndChargeAccounts(1, 1000000)[0]
 	bucketName := storageutils.GenRandomBucketName()
 	msgCreateBucket := storagetypes.NewMsgCreateBucket(
 		user.GetAddr(), bucketName, v, sp.OperatorKey.GetAddr(),
 		nil, math.MaxUint, nil, 0)
+	msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
 	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user, msgCreateBucket)
@@ -1357,7 +932,7 @@ func (s *StorageTestSuite) createObjectWithVisibility(v storagetypes.VisibilityT
 	s.Require().NoError(err)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.BucketName, bucketName)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.Owner, user.GetAddr().String())
-	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PrimarySpAddress, sp.OperatorKey.GetAddr().String())
+	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PrimarySpId, sp.Info.Id)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PaymentAddress, user.GetAddr().String())
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.Visibility, v)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.SourceType, storagetypes.SOURCE_TYPE_ORIGIN)
@@ -1384,7 +959,7 @@ func (s *StorageTestSuite) createObjectWithVisibility(v storagetypes.VisibilityT
 	checksum := sdk.Keccak256(buffer.Bytes())
 	expectChecksum := [][]byte{checksum, checksum, checksum, checksum, checksum, checksum, checksum}
 	contextType := "text/event-stream"
-	msgCreateObject := storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, objectName, uint64(payloadSize), v, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil, nil)
+	msgCreateObject := storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, objectName, uint64(payloadSize), v, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil)
 	msgCreateObject.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateObject.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user, msgCreateObject)
@@ -1408,21 +983,25 @@ func (s *StorageTestSuite) createObjectWithVisibility(v storagetypes.VisibilityT
 	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.ContentType, contextType)
 
 	// SealObject
-	secondarySPs := []sdk.AccAddress{
-		sp.OperatorKey.GetAddr(), sp.OperatorKey.GetAddr(),
-		sp.OperatorKey.GetAddr(), sp.OperatorKey.GetAddr(),
-		sp.OperatorKey.GetAddr(), sp.OperatorKey.GetAddr(),
+	gvgId := gvg.Id
+	msgSealObject := storagetypes.NewMsgSealObject(sp.SealKey.GetAddr(), bucketName, objectName, gvgId, nil)
+
+	secondarySigs := make([][]byte, 0)
+	secondarySPBlsPubKeys := make([]bls.PublicKey, 0)
+	blsSignHash := storagetypes.NewSecondarySpSealObjectSignDoc(s.GetChainID(), gvgId, queryHeadObjectResponse.ObjectInfo.Id, storagetypes.GenerateHash(queryHeadObjectResponse.ObjectInfo.Checksums[:])).GetBlsSignHash()
+	// every secondary sp signs the checksums
+	for i := 1; i < len(s.StorageProviders); i++ {
+		sig, err := core.BlsSignAndVerify(s.StorageProviders[i], blsSignHash)
+		s.Require().NoError(err)
+		secondarySigs = append(secondarySigs, sig)
+		pk, err := bls.PublicKeyFromBytes(s.StorageProviders[i].BlsKey.PubKey().Bytes())
+		s.Require().NoError(err)
+		secondarySPBlsPubKeys = append(secondarySPBlsPubKeys, pk)
 	}
-	msgSealObject := storagetypes.NewMsgSealObject(sp.SealKey.GetAddr(), bucketName, objectName, secondarySPs, nil)
-	sr := storagetypes.NewSecondarySpSignDoc(sp.OperatorKey.GetAddr(), queryHeadObjectResponse.ObjectInfo.Id, checksum)
-	secondarySig, err := sp.ApprovalKey.Sign(sr.GetSignBytes())
-	s.Require().NoError(err)
-	err = storagetypes.VerifySignature(s.StorageProviders[0].ApprovalKey.GetAddr(), sdk.Keccak256(sr.GetSignBytes()),
-		secondarySig)
+	aggBlsSig, err := core.BlsAggregateAndVerify(secondarySPBlsPubKeys, blsSignHash, secondarySigs)
 	s.Require().NoError(err)
+	msgSealObject.SecondarySpBlsAggSignatures = aggBlsSig
 
-	secondarySigs := [][]byte{secondarySig, secondarySig, secondarySig, secondarySig, secondarySig, secondarySig}
-	msgSealObject.SecondarySpSignatures = secondarySigs
 	s.T().Logf("msg %s", msgSealObject.String())
 	s.SendTxBlock(sp.SealKey, msgSealObject)
 
@@ -1559,11 +1138,14 @@ func (s *StorageTestSuite) TestCancelCreateObject() {
 	var err error
 	// CreateBucket
 	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
 	user := s.GenAndChargeAccounts(1, 1000000)[0]
 	bucketName := storageutils.GenRandomBucketName()
 	msgCreateBucket := storagetypes.NewMsgCreateBucket(
 		user.GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PRIVATE, sp.OperatorKey.GetAddr(),
 		nil, math.MaxUint, nil, 0)
+	msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
 	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user, msgCreateBucket)
@@ -1577,7 +1159,7 @@ func (s *StorageTestSuite) TestCancelCreateObject() {
 	s.Require().NoError(err)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.BucketName, bucketName)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.Owner, user.GetAddr().String())
-	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PrimarySpAddress, sp.OperatorKey.GetAddr().String())
+	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PrimarySpId, sp.Info.Id)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PaymentAddress, user.GetAddr().String())
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.Visibility, storagetypes.VISIBILITY_TYPE_PRIVATE)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.SourceType, storagetypes.SOURCE_TYPE_ORIGIN)
@@ -1594,7 +1176,7 @@ func (s *StorageTestSuite) TestCancelCreateObject() {
 	checksum := sdk.Keccak256(buffer.Bytes())
 	expectChecksum := [][]byte{checksum, checksum, checksum, checksum, checksum, checksum, checksum}
 	contextType := "text/event-stream"
-	msgCreateObject := storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, objectName, uint64(payloadSize), storagetypes.VISIBILITY_TYPE_PRIVATE, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil, nil)
+	msgCreateObject := storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, objectName, uint64(payloadSize), storagetypes.VISIBILITY_TYPE_PRIVATE, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil)
 	msgCreateObject.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateObject.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user, msgCreateObject)
@@ -1616,7 +1198,7 @@ func (s *StorageTestSuite) TestCancelCreateObject() {
 	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.SourceType, storagetypes.SOURCE_TYPE_ORIGIN)
 	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.RedundancyType, storagetypes.REDUNDANCY_EC_TYPE)
 	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.ContentType, contextType)
-	s.Require().Equal(len(queryHeadObjectResponse.ObjectInfo.SecondarySpAddresses), 0)
+	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.Creator, "")
 	// CancelCreateObject
 	msgCancelCreateObject := storagetypes.NewMsgCancelCreateObject(user.GetAddr(), bucketName, objectName)
 	s.Require().NoError(err)
@@ -1627,11 +1209,14 @@ func (s *StorageTestSuite) TestCreateObjectWithCommonPrefix() {
 	var err error
 	// CreateBucket
 	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
 	user := s.GenAndChargeAccounts(1, 1000000)[0]
 	bucketName := storageutils.GenRandomBucketName()
 	msgCreateBucket := storagetypes.NewMsgCreateBucket(
 		user.GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PRIVATE, sp.OperatorKey.GetAddr(),
 		nil, math.MaxUint, nil, 0)
+	msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
 	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user, msgCreateBucket)
@@ -1645,7 +1230,7 @@ func (s *StorageTestSuite) TestCreateObjectWithCommonPrefix() {
 	s.Require().NoError(err)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.BucketName, bucketName)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.Owner, user.GetAddr().String())
-	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PrimarySpAddress, sp.OperatorKey.GetAddr().String())
+	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PrimarySpId, sp.Info.Id)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PaymentAddress, user.GetAddr().String())
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.Visibility, storagetypes.VISIBILITY_TYPE_PRIVATE)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.SourceType, storagetypes.SOURCE_TYPE_ORIGIN)
@@ -1659,7 +1244,7 @@ func (s *StorageTestSuite) TestCreateObjectWithCommonPrefix() {
 	checksum := sdk.Keccak256(buffer.Bytes())
 	expectChecksum := [][]byte{checksum, checksum, checksum, checksum, checksum, checksum, checksum}
 	contextType := "text/event-stream"
-	msgCreateObject := storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, objectName, uint64(payloadSize), storagetypes.VISIBILITY_TYPE_PRIVATE, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil, nil)
+	msgCreateObject := storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, objectName, uint64(payloadSize), storagetypes.VISIBILITY_TYPE_PRIVATE, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil)
 	msgCreateObject.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateObject.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user, msgCreateObject)
@@ -1681,7 +1266,6 @@ func (s *StorageTestSuite) TestCreateObjectWithCommonPrefix() {
 	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.SourceType, storagetypes.SOURCE_TYPE_ORIGIN)
 	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.RedundancyType, storagetypes.REDUNDANCY_EC_TYPE)
 	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.ContentType, contextType)
-	s.Require().Equal(len(queryHeadObjectResponse.ObjectInfo.SecondarySpAddresses), 0)
 
 	// CopyObject
 	dstBucketName := bucketName
@@ -1708,7 +1292,6 @@ func (s *StorageTestSuite) TestCreateObjectWithCommonPrefix() {
 	s.Require().Equal(queryCopyObjectHeadObjectResponse.ObjectInfo.SourceType, storagetypes.SOURCE_TYPE_ORIGIN)
 	s.Require().Equal(queryCopyObjectHeadObjectResponse.ObjectInfo.RedundancyType, storagetypes.REDUNDANCY_EC_TYPE)
 	s.Require().Equal(queryCopyObjectHeadObjectResponse.ObjectInfo.ContentType, contextType)
-	s.Require().Equal(len(queryCopyObjectHeadObjectResponse.ObjectInfo.SecondarySpAddresses), 0)
 }
 
 func (s *StorageTestSuite) TestUpdateParams() {
@@ -1839,11 +1422,14 @@ func (s *StorageTestSuite) TestRejectSealObject() {
 	var err error
 	// CreateBucket
 	sp := s.StorageProviders[0]
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
 	user := s.GenAndChargeAccounts(1, 1000000)[0]
 	bucketName := storageutils.GenRandomBucketName()
 	msgCreateBucket := storagetypes.NewMsgCreateBucket(
 		user.GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PRIVATE, sp.OperatorKey.GetAddr(),
 		nil, math.MaxUint, nil, 0)
+	msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
 	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user, msgCreateBucket)
@@ -1857,7 +1443,7 @@ func (s *StorageTestSuite) TestRejectSealObject() {
 	s.Require().NoError(err)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.BucketName, bucketName)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.Owner, user.GetAddr().String())
-	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PrimarySpAddress, sp.OperatorKey.GetAddr().String())
+	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PrimarySpId, sp.Info.Id)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.PaymentAddress, user.GetAddr().String())
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.Visibility, storagetypes.VISIBILITY_TYPE_PRIVATE)
 	s.Require().Equal(queryHeadBucketResponse.BucketInfo.SourceType, storagetypes.SOURCE_TYPE_ORIGIN)
@@ -1874,7 +1460,7 @@ func (s *StorageTestSuite) TestRejectSealObject() {
 	checksum := sdk.Keccak256(buffer.Bytes())
 	expectChecksum := [][]byte{checksum, checksum, checksum, checksum, checksum, checksum, checksum}
 	contextType := "text/event-stream"
-	msgCreateObject := storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, objectName, uint64(payloadSize), storagetypes.VISIBILITY_TYPE_PRIVATE, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil, nil)
+	msgCreateObject := storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, objectName, uint64(payloadSize), storagetypes.VISIBILITY_TYPE_PRIVATE, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil)
 	msgCreateObject.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateObject.GetApprovalBytes())
 	s.Require().NoError(err)
 	s.SendTxBlock(user, msgCreateObject)
@@ -1896,7 +1482,7 @@ func (s *StorageTestSuite) TestRejectSealObject() {
 	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.SourceType, storagetypes.SOURCE_TYPE_ORIGIN)
 	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.RedundancyType, storagetypes.REDUNDANCY_EC_TYPE)
 	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.ContentType, contextType)
-	s.Require().Equal(len(queryHeadObjectResponse.ObjectInfo.SecondarySpAddresses), 0)
+	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.Creator, "")
 	// RejectSealObject
 	msgRejectSealObject := storagetypes.NewMsgRejectUnsealedObject(sp.SealKey.GetAddr(), bucketName, objectName)
 	s.SendTxBlock(sp.SealKey, msgRejectSealObject)
@@ -1910,3 +1496,85 @@ func (s *StorageTestSuite) TestRejectSealObject() {
 	s.Require().Error(err)
 	s.Require().True(strings.Contains(err.Error(), storagetypes.ErrNoSuchObject.Error()))
 }
+
+func (s *StorageTestSuite) TestMigrationBucket() {
+	// construct bucket and object
+	primarySP := s.StorageProviders[0]
+	gvg, found := primarySP.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
+	user := s.GenAndChargeAccounts(1, 1000000)[0]
+	bucketName := storageutils.GenRandomBucketName()
+	objectName := storageutils.GenRandomObjectName()
+	_, _, _, bucketInfo := s.BaseSuite.CreateObject(user, &primarySP, gvg.Id, bucketName, objectName)
+
+	var err error
+	dstPrimarySP := s.CreateNewStorageProvider()
+
+	// migrate bucket
+	msgMigrationBucket := storagetypes.NewMsgMigrateBucket(user.GetAddr(), bucketName, dstPrimarySP.Info.Id)
+	msgMigrationBucket.DstPrimarySpApproval.ExpiredHeight = math.MaxInt
+	msgMigrationBucket.DstPrimarySpApproval.Sig, err = dstPrimarySP.ApprovalKey.Sign(msgMigrationBucket.GetApprovalBytes())
+	s.SendTxBlock(user, msgMigrationBucket)
+	s.Require().NoError(err)
+
+	// cancel migration bucket
+	msgCancelMigrationBucket := storagetypes.NewMsgCancelMigrateBucket(user.GetAddr(), bucketName)
+	s.SendTxBlock(user, msgCancelMigrationBucket)
+	s.Require().NoError(err)
+
+	// complete migration bucket
+	var secondarySPIDs []uint32
+	var secondarySPs []core.StorageProvider
+
+	for _, ssp := range s.StorageProviders {
+		if ssp.Info.Id != primarySP.Info.Id {
+			secondarySPIDs = append(secondarySPIDs, ssp.Info.Id)
+			secondarySPs = append(secondarySPs, ssp)
+		}
+		if len(secondarySPIDs) == 5 {
+			break
+		}
+	}
+	gvgID, _ := s.BaseSuite.CreateGlobalVirtualGroup(dstPrimarySP, 0, secondarySPIDs, 1)
+	gvgResp, err := s.Client.VirtualGroupQueryClient.GlobalVirtualGroup(context.Background(), &types2.QueryGlobalVirtualGroupRequest{
+		GlobalVirtualGroupId: gvgID,
+	})
+	s.Require().NoError(err)
+	dstGVG := gvgResp.GlobalVirtualGroup
+	s.Require().True(found)
+
+	// construct the signatures
+	var gvgMappings []*storagetypes.GVGMapping
+	gvgMappings = append(gvgMappings, &storagetypes.GVGMapping{SrcGlobalVirtualGroupId: gvg.Id, DstGlobalVirtualGroupId: dstGVG.Id})
+	for _, gvgMapping := range gvgMappings {
+		migrationBucketSignHash := storagetypes.NewSecondarySpMigrationBucketSignDoc(s.GetChainID(), bucketInfo.Id, dstPrimarySP.Info.Id, gvgMapping.SrcGlobalVirtualGroupId, gvgMapping.DstGlobalVirtualGroupId).GetBlsSignHash()
+		secondarySigs := make([][]byte, 0)
+		secondarySPBlsPubKeys := make([]bls.PublicKey, 0)
+		for _, ssp := range secondarySPs {
+			sig, err := core.BlsSignAndVerify(ssp, migrationBucketSignHash)
+			s.Require().NoError(err)
+			secondarySigs = append(secondarySigs, sig)
+			pk, err := bls.PublicKeyFromBytes(ssp.BlsKey.PubKey().Bytes())
+			s.Require().NoError(err)
+			secondarySPBlsPubKeys = append(secondarySPBlsPubKeys, pk)
+		}
+		aggBlsSig, err := core.BlsAggregateAndVerify(secondarySPBlsPubKeys, migrationBucketSignHash, secondarySigs)
+		s.Require().NoError(err)
+		gvgMapping.SecondarySpBlsSignature = aggBlsSig
+	}
+
+	msgCompleteMigrationBucket := storagetypes.NewMsgCompleteMigrateBucket(dstPrimarySP.OperatorKey.GetAddr(), bucketName, dstGVG.FamilyId, gvgMappings)
+	s.SendTxBlockWithExpectErrorString(msgCompleteMigrationBucket, dstPrimarySP.OperatorKey, "The bucket is not been migrating")
+
+	// send again
+	msgMigrationBucket = storagetypes.NewMsgMigrateBucket(user.GetAddr(), bucketName, dstPrimarySP.Info.Id)
+	msgMigrationBucket.DstPrimarySpApproval.ExpiredHeight = math.MaxInt
+	msgMigrationBucket.DstPrimarySpApproval.Sig, err = dstPrimarySP.ApprovalKey.Sign(msgMigrationBucket.GetApprovalBytes())
+	s.SendTxBlock(user, msgMigrationBucket)
+	s.Require().NoError(err)
+
+	// complete again
+	msgCompleteMigrationBucket = storagetypes.NewMsgCompleteMigrateBucket(dstPrimarySP.OperatorKey.GetAddr(), bucketName, dstGVG.FamilyId, gvgMappings)
+	s.SendTxBlock(dstPrimarySP.OperatorKey, msgCompleteMigrationBucket)
+
+}
diff --git a/e2e/tests/virtualgroup_test.go b/e2e/tests/virtualgroup_test.go
new file mode 100644
index 000000000..f6caef2c8
--- /dev/null
+++ b/e2e/tests/virtualgroup_test.go
@@ -0,0 +1,546 @@
+package tests
+
+import (
+	"bytes"
+	"context"
+	"fmt"
+	"math"
+	"testing"
+	"time"
+
+	sdkmath "cosmossdk.io/math"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	types2 "github.com/cosmos/cosmos-sdk/x/bank/types"
+	"github.com/prysmaticlabs/prysm/crypto/bls"
+	"github.com/stretchr/testify/suite"
+
+	"github.com/bnb-chain/greenfield/e2e/core"
+	"github.com/bnb-chain/greenfield/sdk/types"
+	storagetestutil "github.com/bnb-chain/greenfield/testutil/storage"
+	"github.com/bnb-chain/greenfield/types/common"
+	sptypes "github.com/bnb-chain/greenfield/x/sp/types"
+	storagetypes "github.com/bnb-chain/greenfield/x/storage/types"
+	virtualgroupmoduletypes "github.com/bnb-chain/greenfield/x/virtualgroup/types"
+)
+
+type VirtualGroupTestSuite struct {
+	core.BaseSuite
+}
+
+func (s *VirtualGroupTestSuite) SetupSuite() {
+	s.BaseSuite.SetupSuite()
+}
+
+func (s *VirtualGroupTestSuite) SetupTest() {
+}
+
+func TestVirtualGroupTestSuite(t *testing.T) {
+	suite.Run(t, new(VirtualGroupTestSuite))
+}
+
+func (s *VirtualGroupTestSuite) queryGlobalVirtualGroup(gvgID uint32) *virtualgroupmoduletypes.GlobalVirtualGroup {
+	resp, err := s.Client.GlobalVirtualGroup(
+		context.Background(),
+		&virtualgroupmoduletypes.QueryGlobalVirtualGroupRequest{GlobalVirtualGroupId: gvgID})
+	s.Require().NoError(err)
+	return resp.GlobalVirtualGroup
+}
+
+func (s *VirtualGroupTestSuite) queryGlobalVirtualGroupByFamily(spID, familyID uint32) []*virtualgroupmoduletypes.GlobalVirtualGroup {
+	resp, err := s.Client.GlobalVirtualGroupByFamilyID(
+		context.Background(),
+		&virtualgroupmoduletypes.QueryGlobalVirtualGroupByFamilyIDRequest{
+			StorageProviderId:          spID,
+			GlobalVirtualGroupFamilyId: familyID,
+		})
+	s.Require().NoError(err)
+	return resp.GlobalVirtualGroups
+}
+
+func (s *VirtualGroupTestSuite) queryGlobalVirtualGroupFamilies(spID uint32) []*virtualgroupmoduletypes.GlobalVirtualGroupFamily {
+	resp, err := s.Client.GlobalVirtualGroupFamilies(
+		context.Background(),
+		&virtualgroupmoduletypes.QueryGlobalVirtualGroupFamiliesRequest{StorageProviderId: spID})
+	s.Require().NoError(err)
+	return resp.GlobalVirtualGroupFamilies
+}
+
+func (s *VirtualGroupTestSuite) TestBasic() {
+	primarySP := s.StorageProviders[0]
+
+	gvgFamilies := s.queryGlobalVirtualGroupFamilies(primarySP.Info.Id)
+	s.Require().Greater(len(gvgFamilies), 0)
+
+	family := gvgFamilies[0]
+	s.T().Log(family.String())
+
+	var secondarySPIDs []uint32
+	for _, ssp := range s.StorageProviders {
+		if ssp.Info.Id != primarySP.Info.Id {
+			secondarySPIDs = append(secondarySPIDs, ssp.Info.Id)
+		}
+	}
+	s.BaseSuite.CreateGlobalVirtualGroup(&primarySP, family.Id, secondarySPIDs, 1)
+
+	gvgs := s.queryGlobalVirtualGroupByFamily(primarySP.Info.Id, family.Id)
+	s.Require().Equal(len(gvgs), len(family.GlobalVirtualGroupIds)+1)
+
+	oldGVGIDs := make(map[uint32]bool)
+	for _, id := range family.GlobalVirtualGroupIds {
+		oldGVGIDs[id] = true
+	}
+	var newGVG *virtualgroupmoduletypes.GlobalVirtualGroup
+
+	for _, gvg := range gvgs {
+		if !oldGVGIDs[gvg.Id] {
+			newGVG = gvg
+			break
+		}
+	}
+
+	s.Require().Equal(newGVG.TotalDeposit.Int64(), int64(1000000000000000000))
+
+	// test deposit
+	msgDeposit := virtualgroupmoduletypes.MsgDeposit{
+		StorageProvider:      primarySP.FundingKey.GetAddr().String(),
+		GlobalVirtualGroupId: newGVG.Id,
+		Deposit:              sdk.NewCoin(s.Config.Denom, types.NewIntFromInt64WithDecimal(1, types.DecimalBNB)),
+	}
+	s.SendTxBlock(primarySP.FundingKey, &msgDeposit)
+
+	gvgAfterDeposit := s.queryGlobalVirtualGroup(newGVG.Id)
+	s.Require().Equal(gvgAfterDeposit.TotalDeposit.Int64(), int64(2000000000000000000))
+
+	// test withdraw
+	balance, err := s.Client.BankQueryClient.Balance(context.Background(), &types2.QueryBalanceRequest{
+		Denom: s.Config.Denom, Address: primarySP.FundingKey.GetAddr().String()})
+	s.Require().NoError(err)
+
+	msgWithdraw := virtualgroupmoduletypes.MsgWithdraw{
+		StorageProvider:      primarySP.FundingKey.GetAddr().String(),
+		Withdraw:             sdk.NewCoin(s.Config.Denom, types.NewIntFromInt64WithDecimal(1, types.DecimalBNB)),
+		GlobalVirtualGroupId: newGVG.Id,
+	}
+	s.SendTxBlock(primarySP.FundingKey, &msgWithdraw)
+	balanceAfterWithdraw, err := s.Client.BankQueryClient.Balance(context.Background(), &types2.QueryBalanceRequest{
+		Denom: s.Config.Denom, Address: primarySP.FundingKey.GetAddr().String()})
+	s.Require().NoError(err)
+
+	s.T().Logf("balance: %s, after: %s", balance.String(), balanceAfterWithdraw.String())
+	s.Require().Equal(balanceAfterWithdraw.Balance.Amount.Sub(balance.Balance.Amount).Int64(), int64(999994000000000000))
+
+	// test delete gvg
+	msgDeleteGVG := virtualgroupmoduletypes.MsgDeleteGlobalVirtualGroup{
+		StorageProvider:      primarySP.OperatorKey.GetAddr().String(),
+		GlobalVirtualGroupId: newGVG.Id,
+	}
+	s.SendTxBlock(primarySP.OperatorKey, &msgDeleteGVG)
+
+	newGVGs := s.queryGlobalVirtualGroupByFamily(primarySP.Info.Id, family.Id)
+
+	for _, gvg := range newGVGs {
+		if gvg.Id == newGVG.Id {
+			s.Assert().True(false)
+		}
+	}
+	_, err = s.Client.GlobalVirtualGroup(
+		context.Background(),
+		&virtualgroupmoduletypes.QueryGlobalVirtualGroupRequest{GlobalVirtualGroupId: newGVG.Id})
+	s.Require().Error(err)
+}
+
+func (s *VirtualGroupTestSuite) TestSettle() {
+	_, _, primarySp, secondarySps, gvgFamilyId, gvgId := s.createObject()
+	s.T().Log("gvg family", gvgFamilyId, "gvg", gvgId)
+
+	queryFamilyResp, err := s.Client.GlobalVirtualGroupFamily(
+		context.Background(),
+		&virtualgroupmoduletypes.QueryGlobalVirtualGroupFamilyRequest{
+			StorageProviderId: primarySp.Info.Id,
+			FamilyId:          gvgFamilyId,
+		})
+	s.Require().NoError(err)
+	gvgFamily := queryFamilyResp.GlobalVirtualGroupFamily
+
+	queryGVGResp, err := s.Client.GlobalVirtualGroup(
+		context.Background(),
+		&virtualgroupmoduletypes.QueryGlobalVirtualGroupRequest{
+			GlobalVirtualGroupId: gvgId,
+		})
+	s.Require().NoError(err)
+	secondarySpIds := make(map[uint32]struct{})
+	for _, id := range queryGVGResp.GlobalVirtualGroup.SecondarySpIds {
+		secondarySpIds[id] = struct{}{}
+	}
+
+	var lastSecondarySp *core.StorageProvider
+	secondarySpAddrs := make([]string, 0)
+	for _, secondarySp := range secondarySps {
+		if _, ok := secondarySpIds[secondarySp.Info.Id]; ok {
+			secondarySpAddrs = append(secondarySpAddrs, secondarySp.FundingKey.GetAddr().String())
+			lastSecondarySp = &secondarySp
+		}
+	}
+
+	// sleep seconds
+	time.Sleep(3 * time.Second)
+
+	primaryBalance, err := s.Client.BankQueryClient.Balance(context.Background(), &types2.QueryBalanceRequest{
+		Denom: s.Config.Denom, Address: primarySp.FundingKey.GetAddr().String()})
+	s.Require().NoError(err)
+	secondaryBalances := make([]sdkmath.Int, 0)
+	for _, addr := range secondarySpAddrs {
+		tempResp, err := s.Client.BankQueryClient.Balance(context.Background(), &types2.QueryBalanceRequest{
+			Denom: s.Config.Denom, Address: addr})
+		s.Require().NoError(err)
+		secondaryBalances = append(secondaryBalances, tempResp.Balance.Amount)
+	}
+
+	// settle gvg family
+	msgSettle := virtualgroupmoduletypes.MsgSettle{
+		StorageProvider:            primarySp.FundingKey.GetAddr().String(),
+		GlobalVirtualGroupFamilyId: gvgFamily.Id,
+	}
+
+	simulateRes := s.SimulateTx(&msgSettle, primarySp.FundingKey)
+	gasLimit := simulateRes.GasInfo.GetGasUsed()
+	gasPrice, _ := sdk.ParseCoinNormalized(simulateRes.GasInfo.GetMinGasPrice())
+	feeAmount := gasPrice.Amount.Mul(sdk.NewInt(int64(gasLimit)))
+	s.T().Logf("fee amount: %s", feeAmount.String())
+
+	s.SendTxBlock(primarySp.FundingKey, &msgSettle)
+
+	primaryBalanceAfter, err := s.Client.BankQueryClient.Balance(context.Background(), &types2.QueryBalanceRequest{
+		Denom: s.Config.Denom, Address: primarySp.FundingKey.GetAddr().String()})
+	s.Require().NoError(err)
+
+	s.T().Logf("primaryBalance: %s, after: %s", primaryBalance.String(), primaryBalanceAfter.String())
+	s.Require().True(primaryBalanceAfter.Balance.Amount.Add(feeAmount).GT(primaryBalance.Balance.Amount))
+
+	// settle gvg
+	msgSettle = virtualgroupmoduletypes.MsgSettle{
+		StorageProvider:            lastSecondarySp.FundingKey.GetAddr().String(),
+		GlobalVirtualGroupFamilyId: 0,
+		GlobalVirtualGroupIds:      []uint32{gvgId},
+	}
+
+	simulateRes = s.SimulateTx(&msgSettle, lastSecondarySp.FundingKey)
+	gasLimit = simulateRes.GasInfo.GetGasUsed()
+	gasPrice, _ = sdk.ParseCoinNormalized(simulateRes.GasInfo.GetMinGasPrice())
+	feeAmount = gasPrice.Amount.Mul(sdk.NewInt(int64(gasLimit)))
+	s.T().Logf("fee amount: %s", feeAmount.String())
+
+	s.SendTxBlock(lastSecondarySp.FundingKey, &msgSettle)
+
+	secondaryBalancesAfter := make([]sdkmath.Int, 0, len(secondaryBalances))
+	for _, addr := range secondarySpAddrs {
+		tempResp, err := s.Client.BankQueryClient.Balance(context.Background(), &types2.QueryBalanceRequest{
+			Denom: s.Config.Denom, Address: addr})
+		s.Require().NoError(err)
+		secondaryBalancesAfter = append(secondaryBalancesAfter, tempResp.Balance.Amount)
+	}
+
+	for i := range secondaryBalances {
+		s.T().Logf("secondaryBalance: %s, after: %s", secondaryBalances[i].String(), secondaryBalancesAfter[i].String())
+		if i != len(secondaryBalances)-1 {
+			s.Require().True(secondaryBalancesAfter[i].GT(secondaryBalances[i]))
+		} else {
+			s.Require().True(secondaryBalancesAfter[i].Add(feeAmount).GT(secondaryBalances[i]))
+		}
+	}
+}
+
+func (s *VirtualGroupTestSuite) createObject() (string, string, core.StorageProvider, []core.StorageProvider, uint32, uint32) {
+	var err error
+	sp := s.StorageProviders[0]
+	secondarySps := make([]core.StorageProvider, 0)
+	gvg, found := sp.GetFirstGlobalVirtualGroup()
+	s.Require().True(found)
+
+	// CreateBucket
+	user := s.GenAndChargeAccounts(1, 1000000)[0]
+	bucketName := "ch" + storagetestutil.GenRandomBucketName()
+	msgCreateBucket := storagetypes.NewMsgCreateBucket(
+		user.GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PRIVATE, sp.OperatorKey.GetAddr(),
+		nil, math.MaxUint, nil, 0)
+	msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId
+	msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes())
+	s.Require().NoError(err)
+	s.SendTxBlock(user, msgCreateBucket)
+
+	// HeadBucket
+	ctx := context.Background()
+	queryHeadBucketRequest := storagetypes.QueryHeadBucketRequest{
+		BucketName: bucketName,
+	}
+	queryHeadBucketResponse, err := s.Client.HeadBucket(ctx, &queryHeadBucketRequest)
+	s.Require().NoError(err)
+	s.Require().Equal(queryHeadBucketResponse.BucketInfo.BucketName, bucketName)
+
+	// CreateObject
+	objectName := storagetestutil.GenRandomObjectName()
+	// create test buffer
+	var buffer bytes.Buffer
+	line := `1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,
+	1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,
+	1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,
+	1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,
+	1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,
+	1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,
+	1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,
+	1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,
+	1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,
+	1234567890,1234567890,1234567890,123`
+	// Create 1MiB content where each line contains 1024 characters.
+	for i := 0; i < 1024; i++ {
+		buffer.WriteString(fmt.Sprintf("[%05d] %s\n", i, line))
+	}
+	payloadSize := buffer.Len()
+	checksum := sdk.Keccak256(buffer.Bytes())
+	expectChecksum := [][]byte{checksum, checksum, checksum, checksum, checksum, checksum, checksum}
+	contextType := "text/event-stream"
+	msgCreateObject := storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, objectName, uint64(payloadSize), storagetypes.VISIBILITY_TYPE_PRIVATE, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil)
+	msgCreateObject.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateObject.GetApprovalBytes())
+	s.Require().NoError(err)
+	s.SendTxBlock(user, msgCreateObject)
+
+	// HeadObject
+	queryHeadObjectRequest := storagetypes.QueryHeadObjectRequest{
+		BucketName: bucketName,
+		ObjectName: objectName,
+	}
+	queryHeadObjectResponse, err := s.Client.HeadObject(ctx, &queryHeadObjectRequest)
+	s.Require().NoError(err)
+	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.ObjectName, objectName)
+	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.BucketName, bucketName)
+
+	// SealObject
+	gvgId := gvg.Id
+	msgSealObject := storagetypes.NewMsgSealObject(sp.SealKey.GetAddr(), bucketName, objectName, gvg.Id, nil)
+
+	secondarySigs := make([][]byte, 0)
+	secondarySPBlsPubKeys := make([]bls.PublicKey, 0)
+	blsSignHash := storagetypes.NewSecondarySpSealObjectSignDoc(s.GetChainID(), gvgId, queryHeadObjectResponse.ObjectInfo.Id, storagetypes.GenerateHash(queryHeadObjectResponse.ObjectInfo.Checksums[:])).GetBlsSignHash()
+	// every secondary sp signs the checksums
+	for i := 1; i < len(s.StorageProviders); i++ {
+		sig, err := core.BlsSignAndVerify(s.StorageProviders[i], blsSignHash)
+		s.Require().NoError(err)
+		secondarySigs = append(secondarySigs, sig)
+		pk, err := bls.PublicKeyFromBytes(s.StorageProviders[i].BlsKey.PubKey().Bytes())
+		s.Require().NoError(err)
+		secondarySPBlsPubKeys = append(secondarySPBlsPubKeys, pk)
+		if s.StorageProviders[i].Info.Id != sp.Info.Id {
+			secondarySps = append(secondarySps, s.StorageProviders[i])
+		}
+	}
+	aggBlsSig, err := core.BlsAggregateAndVerify(secondarySPBlsPubKeys, blsSignHash, secondarySigs)
+	s.Require().NoError(err)
+	msgSealObject.SecondarySpBlsAggSignatures = aggBlsSig
+	s.SendTxBlock(sp.SealKey, msgSealObject)
+
+	queryHeadObjectResponse, err = s.Client.HeadObject(ctx, &queryHeadObjectRequest)
+	s.Require().NoError(err)
+	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.ObjectName, objectName)
+	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.BucketName, bucketName)
+	s.Require().Equal(queryHeadObjectResponse.ObjectInfo.ObjectStatus, storagetypes.OBJECT_STATUS_SEALED)
+
+	return bucketName, objectName, sp, secondarySps, gvg.FamilyId, gvg.Id
+}
+
+func (s *VirtualGroupTestSuite) TestSPExit() {
+	user := s.GenAndChargeAccounts(1, 1000000)[0]
+	// 1, create a new storage provider
+	sp := s.BaseSuite.CreateNewStorageProvider()
+	s.T().Logf("new SP Info: %s", sp.Info.String())
+
+	successorSp := s.StorageProviders[0]
+
+	// 2, create a new gvg group for this storage provider
+	var secondarySPIDs []uint32
+	for _, ssp := range s.StorageProviders {
+		if ssp.Info.Id != successorSp.Info.Id {
+			secondarySPIDs = append(secondarySPIDs, ssp.Info.Id)
+		}
+		if len(secondarySPIDs) == 6 {
+			break
+		}
+	}
+
+	gvgID, familyID := s.BaseSuite.CreateGlobalVirtualGroup(sp, 0, secondarySPIDs, 1)
+
+	// 3. create object
+	s.BaseSuite.CreateObject(user, sp, gvgID, storagetestutil.GenRandomBucketName(), storagetestutil.GenRandomObjectName())
+
+	// 4. Create another gvg contains this new sp
+	anotherSP := s.StorageProviders[1]
+	var anotherSecondarySPIDs []uint32
+	for _, ssp := range s.StorageProviders {
+		if ssp.Info.Id != successorSp.Info.Id && ssp.Info.Id != anotherSP.Info.Id {
+			anotherSecondarySPIDs = append(anotherSecondarySPIDs, ssp.Info.Id)
+		}
+		if len(anotherSecondarySPIDs) == 5 {
+			break
+		}
+	}
+	anotherSecondarySPIDs = append(anotherSecondarySPIDs, sp.Info.Id)
+
+	anotherSPsFamilies := s.queryGlobalVirtualGroupFamilies(anotherSP.Info.Id)
+	s.Require().Greater(len(anotherSPsFamilies), 0)
+	anotherGVGID, _ := s.BaseSuite.CreateGlobalVirtualGroup(&anotherSP, anotherSPsFamilies[0].Id, anotherSecondarySPIDs, 1)
+
+	// 5. sp exit
+	s.SendTxBlock(sp.OperatorKey, &virtualgroupmoduletypes.MsgStorageProviderExit{
+		StorageProvider: sp.OperatorKey.GetAddr().String(),
+	})
+
+	resp, err := s.Client.StorageProvider(context.Background(), &sptypes.QueryStorageProviderRequest{Id: sp.Info.Id})
+	s.Require().NoError(err)
+	s.Require().Equal(resp.StorageProvider.Status, sptypes.STATUS_GRACEFUL_EXITING)
+
+	// 6. sp complete exit failed
+	s.SendTxBlockWithExpectErrorString(
+		&virtualgroupmoduletypes.MsgCompleteStorageProviderExit{StorageProvider: sp.OperatorKey.GetAddr().String()},
+		sp.OperatorKey,
+		"not swap out from all the family")
+
+	// 7. swap out, as primary sp
+	msgSwapOut := virtualgroupmoduletypes.NewMsgSwapOut(sp.OperatorKey.GetAddr(), familyID, nil, successorSp.Info.Id)
+	msgSwapOut.SuccessorSpApproval = &common.Approval{ExpiredHeight: math.MaxUint}
+	msgSwapOut.SuccessorSpApproval.Sig, err = successorSp.ApprovalKey.Sign(msgSwapOut.GetApprovalBytes())
+	s.Require().NoError(err)
+	s.SendTxBlock(sp.OperatorKey, msgSwapOut)
+
+	// 9. cancel swap out
+	msgCancelSwapOut := virtualgroupmoduletypes.NewMsgCancelSwapOut(sp.OperatorKey.GetAddr(), familyID, nil)
+	s.Require().NoError(err)
+	s.SendTxBlock(sp.OperatorKey, msgCancelSwapOut)
+
+	// 10. complete swap out, as primary sp
+	msgCompleteSwapOut := virtualgroupmoduletypes.NewMsgCompleteSwapOut(successorSp.OperatorKey.GetAddr(), familyID, nil)
+	s.Require().NoError(err)
+	s.SendTxBlockWithExpectErrorString(msgCompleteSwapOut, successorSp.OperatorKey, "The swap info not found in blockchain")
+
+	// 11 swap again
+	msgSwapOut = virtualgroupmoduletypes.NewMsgSwapOut(sp.OperatorKey.GetAddr(), familyID, nil, successorSp.Info.Id)
+	msgSwapOut.SuccessorSpApproval = &common.Approval{ExpiredHeight: math.MaxUint}
+	msgSwapOut.SuccessorSpApproval.Sig, err = successorSp.ApprovalKey.Sign(msgSwapOut.GetApprovalBytes())
+	s.Require().NoError(err)
+	s.SendTxBlock(sp.OperatorKey, msgSwapOut)
+
+	// 12. sp complete exit failed
+	s.SendTxBlockWithExpectErrorString(
+		&virtualgroupmoduletypes.MsgCompleteStorageProviderExit{StorageProvider: sp.OperatorKey.GetAddr().String()},
+		sp.OperatorKey,
+		"not swap out from all the family")
+
+	// 13. complete swap out, as primary sp
+	msgCompleteSwapOut = virtualgroupmoduletypes.NewMsgCompleteSwapOut(successorSp.OperatorKey.GetAddr(), familyID, nil)
+	s.Require().NoError(err)
+	s.SendTxBlock(successorSp.OperatorKey, msgCompleteSwapOut)
+
+	// 14. exist failed
+	s.SendTxBlockWithExpectErrorString(
+		&virtualgroupmoduletypes.MsgCompleteStorageProviderExit{StorageProvider: sp.OperatorKey.GetAddr().String()},
+		sp.OperatorKey,
+		"not swap out from all the gvgs")
+
+	// 15. swap out, as secondary sp
+	msgSwapOut2 := virtualgroupmoduletypes.NewMsgSwapOut(sp.OperatorKey.GetAddr(), 0, []uint32{anotherGVGID}, successorSp.Info.Id)
+	msgSwapOut2.SuccessorSpApproval = &common.Approval{ExpiredHeight: math.MaxUint}
+	msgSwapOut2.SuccessorSpApproval.Sig, err = successorSp.ApprovalKey.Sign(msgSwapOut2.GetApprovalBytes())
+	s.Require().NoError(err)
+	s.SendTxBlock(sp.OperatorKey, msgSwapOut2)
+
+	// 16. exist failed
+	s.SendTxBlockWithExpectErrorString(
+		&virtualgroupmoduletypes.MsgCompleteStorageProviderExit{StorageProvider: sp.OperatorKey.GetAddr().String()},
+		sp.OperatorKey,
+		"not swap out from all the gvgs")
+
+	// 17 cancel swap out as secondary sp
+	msgCancelSwapOut = virtualgroupmoduletypes.NewMsgCancelSwapOut(sp.OperatorKey.GetAddr(), 0, []uint32{anotherGVGID})
+	s.Require().NoError(err)
+	s.SendTxBlock(sp.OperatorKey, msgCancelSwapOut)
+
+	// 18. swap
+	msgCompleteSwapOut2 := virtualgroupmoduletypes.NewMsgCompleteSwapOut(successorSp.OperatorKey.GetAddr(), 0, []uint32{anotherGVGID})
+	s.Require().NoError(err)
+	s.SendTxBlockWithExpectErrorString(msgCompleteSwapOut2, successorSp.OperatorKey, "The swap info not found in blockchain")
+
+	// 19. swap out again, as secondary sp
+	msgSwapOut2 = virtualgroupmoduletypes.NewMsgSwapOut(sp.OperatorKey.GetAddr(), 0, []uint32{anotherGVGID}, successorSp.Info.Id)
+	msgSwapOut2.SuccessorSpApproval = &common.Approval{ExpiredHeight: math.MaxUint}
+	msgSwapOut2.SuccessorSpApproval.Sig, err = successorSp.ApprovalKey.Sign(msgSwapOut2.GetApprovalBytes())
+	s.Require().NoError(err)
+	s.SendTxBlock(sp.OperatorKey, msgSwapOut2)
+
+	// 20 complete swap out
+	msgCompleteSwapOut2 = virtualgroupmoduletypes.NewMsgCompleteSwapOut(successorSp.OperatorKey.GetAddr(), 0, []uint32{anotherGVGID})
+	s.Require().NoError(err)
+	s.SendTxBlock(successorSp.OperatorKey, msgCompleteSwapOut2)
+
+	// 18. sp complete exit success
+	s.SendTxBlock(
+		sp.OperatorKey,
+		&virtualgroupmoduletypes.MsgCompleteStorageProviderExit{StorageProvider: sp.OperatorKey.GetAddr().String()},
+	)
+
+}
+
+func (s *VirtualGroupTestSuite) TestSPExit_CreateAndDeleteBucket() {
+
+	user := s.GenAndChargeAccounts(1, 1000000)[0]
+	bucketName := storagetestutil.GenRandomBucketName()
+	objectName := storagetestutil.GenRandomObjectName()
+	// 1, create a new storage provider
+	sp := s.BaseSuite.CreateNewStorageProvider()
+	s.T().Logf("new SP Info: %s", sp.Info.String())
+
+	successorSp := s.StorageProviders[0]
+
+	// 2, create a new gvg group for this storage provider
+	var secondarySPIDs []uint32
+	for _, ssp := range s.StorageProviders {
+		if ssp.Info.Id != successorSp.Info.Id {
+			secondarySPIDs = append(secondarySPIDs, ssp.Info.Id)
+		}
+		if len(secondarySPIDs) == 6 {
+			break
+		}
+	}
+
+	gvgID, _ := s.BaseSuite.CreateGlobalVirtualGroup(sp, 0, secondarySPIDs, 1)
+
+	// 3. create object
+	s.BaseSuite.CreateObject(user, sp, gvgID, bucketName, objectName)
+
+	// 4. sp apply exit
+	s.SendTxBlock(sp.OperatorKey, &virtualgroupmoduletypes.MsgStorageProviderExit{
+		StorageProvider: sp.OperatorKey.GetAddr().String(),
+	})
+
+	resp, err := s.Client.StorageProvider(context.Background(), &sptypes.QueryStorageProviderRequest{Id: sp.Info.Id})
+	s.Require().NoError(err)
+	s.Require().Equal(resp.StorageProvider.Status, sptypes.STATUS_GRACEFUL_EXITING)
+
+	// 5. sp complete exit failed
+	s.SendTxBlockWithExpectErrorString(
+		&virtualgroupmoduletypes.MsgCompleteStorageProviderExit{StorageProvider: sp.OperatorKey.GetAddr().String()},
+		sp.OperatorKey,
+		"not swap out from all the family")
+
+	// 6. delete object
+	s.SendTxBlock(user, storagetypes.NewMsgDeleteObject(user.GetAddr(), bucketName, objectName))
+
+	// 7. delete bucket
+	s.SendTxBlock(user, storagetypes.NewMsgDeleteBucket(user.GetAddr(), bucketName))
+
+	// 8. delete gvg
+	s.SendTxBlock(sp.OperatorKey, virtualgroupmoduletypes.NewMsgDeleteGlobalVirtualGroup(sp.OperatorKey.GetAddr(), gvgID))
+	// 8. sp complete exit success
+	s.SendTxBlock(
+		sp.OperatorKey,
+		&virtualgroupmoduletypes.MsgCompleteStorageProviderExit{StorageProvider: sp.OperatorKey.GetAddr().String()},
+	)
+
+}
diff --git a/go.mod b/go.mod
index 58e365c17..1642ecbbb 100644
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,7 @@ module github.com/bnb-chain/greenfield
 go 1.19
 
 require (
+	cosmossdk.io/api v0.4.0
 	cosmossdk.io/errors v1.0.0-beta.7
 	cosmossdk.io/math v1.0.1
 	github.com/bits-and-blooms/bitset v1.2.0
@@ -13,30 +14,26 @@ require (
 	github.com/cosmos/go-bip39 v1.0.0
 	github.com/cosmos/gogoproto v1.4.10
 	github.com/ethereum/go-ethereum v1.10.22
+	github.com/ghodss/yaml v1.0.0
+	github.com/golang/mock v1.6.0
 	github.com/golang/protobuf v1.5.3
 	github.com/grpc-ecosystem/grpc-gateway v1.16.0
 	github.com/pkg/errors v0.9.1
 	github.com/prysmaticlabs/prysm v0.0.0-20220124113610-e26cde5e091b
+	github.com/rakyll/statik v0.1.7
 	github.com/samber/lo v1.37.0
 	github.com/spf13/cast v1.5.0
 	github.com/spf13/cobra v1.7.0
 	github.com/spf13/pflag v1.0.5
 	github.com/spf13/viper v1.15.0
-	github.com/stretchr/testify v1.8.2
-	google.golang.org/genproto v0.0.0-20230320184635-7606e756e683
-	google.golang.org/grpc v1.55.0
+	github.com/stretchr/testify v1.8.4
+	google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1
+	google.golang.org/grpc v1.56.1
 	google.golang.org/protobuf v1.30.0
 	gopkg.in/yaml.v2 v2.4.0
 	sigs.k8s.io/yaml v1.3.0
 )
 
-require (
-	cosmossdk.io/api v0.4.0
-	github.com/ghodss/yaml v1.0.0
-	github.com/golang/mock v1.6.0
-	github.com/rakyll/statik v0.1.7
-)
-
 require (
 	cosmossdk.io/core v0.6.1 // indirect
 	cosmossdk.io/depinject v1.0.0-alpha.3 // indirect
@@ -115,6 +112,7 @@ require (
 	github.com/leodido/go-urn v1.2.1 // indirect
 	github.com/lib/pq v1.10.7 // indirect
 	github.com/libp2p/go-buffer-pool v0.1.0 // indirect
+	github.com/linxGnu/grocksdb v1.7.16 // indirect
 	github.com/magiconair/properties v1.8.7 // indirect
 	github.com/manifoldco/promptui v0.9.0 // indirect
 	github.com/mattn/go-colorable v0.1.13 // indirect
@@ -142,13 +140,12 @@ require (
 	github.com/rs/zerolog v1.29.1 // indirect
 	github.com/russross/blackfriday/v2 v2.1.0 // indirect
 	github.com/sasha-s/go-deadlock v0.3.1 // indirect
-	github.com/sirupsen/logrus v1.9.0 // indirect
+	github.com/sirupsen/logrus v1.9.3 // indirect
 	github.com/spf13/afero v1.9.3 // indirect
 	github.com/spf13/jwalterweatherman v1.1.0 // indirect
 	github.com/subosito/gotenv v1.4.2 // indirect
 	github.com/supranational/blst v0.3.8-0.20220526154634-513d2456b344 // indirect
 	github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
-	github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect
 	github.com/tendermint/go-amino v0.16.0 // indirect
 	github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e // indirect
 	github.com/tidwall/btree v1.6.0 // indirect
@@ -163,11 +160,11 @@ require (
 	github.com/zondax/hid v0.9.1 // indirect
 	github.com/zondax/ledger-go v0.14.1 // indirect
 	go.etcd.io/bbolt v1.3.7 // indirect
-	golang.org/x/crypto v0.8.0 // indirect
+	golang.org/x/crypto v0.9.0 // indirect
 	golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect
-	golang.org/x/net v0.9.0 // indirect
-	golang.org/x/sys v0.7.0 // indirect
-	golang.org/x/term v0.7.0 // indirect
+	golang.org/x/net v0.10.0 // indirect
+	golang.org/x/sys v0.8.0 // indirect
+	golang.org/x/term v0.8.0 // indirect
 	golang.org/x/text v0.9.0 // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
@@ -179,8 +176,10 @@ replace (
 	cosmossdk.io/api => github.com/bnb-chain/greenfield-cosmos-sdk/api v0.0.0-20230425074444-eb5869b05fe9
 	cosmossdk.io/math => github.com/bnb-chain/greenfield-cosmos-sdk/math v0.0.0-20230425074444-eb5869b05fe9
 	github.com/btcsuite/btcd => github.com/btcsuite/btcd v0.23.0
-	github.com/cometbft/cometbft => github.com/bnb-chain/greenfield-cometbft v0.0.1
+	github.com/cometbft/cometbft => github.com/bnb-chain/greenfield-cometbft v0.0.2-alpha.1
+	github.com/cometbft/cometbft-db => github.com/bnb-chain/greenfield-cometbft-db v0.8.1-alpha.1
 	github.com/confio/ics23/go => github.com/cosmos/cosmos-sdk/ics23/go v0.8.0
-	github.com/cosmos/cosmos-sdk => github.com/bnb-chain/greenfield-cosmos-sdk v0.2.3-alpha.1
+	github.com/cosmos/cosmos-sdk => github.com/bnb-chain/greenfield-cosmos-sdk v0.2.3-alpha.3
+	github.com/cosmos/iavl => github.com/bnb-chain/greenfield-iavl v0.20.1-alpha.1
 	github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
 )
diff --git a/go.sum b/go.sum
index 5e9b4b971..8c67e84f0 100644
--- a/go.sum
+++ b/go.sum
@@ -161,14 +161,18 @@ github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsy
 github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA=
 github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
 github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c=
-github.com/bnb-chain/greenfield-cometbft v0.0.1 h1:pX8S9oZKjWJCrxH07l6rbU3zee9txZ11+UwO9stsNMQ=
-github.com/bnb-chain/greenfield-cometbft v0.0.1/go.mod h1:9q11eHNRY9FDwFH+4pompzPNGv//Z3VcfvkELaHJPMs=
-github.com/bnb-chain/greenfield-cosmos-sdk v0.2.3-alpha.1 h1:woVNr3N+h/dY7sTZaKscTHARJv4RHLh0+qhniqkF734=
-github.com/bnb-chain/greenfield-cosmos-sdk v0.2.3-alpha.1/go.mod h1:ePxSdTXosDo5YE9TRxqzmXv8T0PH7AVqTfofRf81aRo=
+github.com/bnb-chain/greenfield-cometbft v0.0.2-alpha.1 h1:DU/lvMpzyS5PzLaAcBu1xaQ2/ezuxcJFdi0ej3ZeAsc=
+github.com/bnb-chain/greenfield-cometbft v0.0.2-alpha.1/go.mod h1:EBmwmUdaNbGPyGjf1cMuoN3pAeM2tQu7Lfg95813EAw=
+github.com/bnb-chain/greenfield-cometbft-db v0.8.1-alpha.1 h1:XcWulGacHVRiSCx90Q8Y//ajOrLNBQWR/KDB89dy3cU=
+github.com/bnb-chain/greenfield-cometbft-db v0.8.1-alpha.1/go.mod h1:ey1CiK4bYo1RBNJLRiVbYr5CMdSxci9S/AZRINLtppI=
+github.com/bnb-chain/greenfield-cosmos-sdk v0.2.3-alpha.3 h1:b7guOnZt6gFS3HjYITV3x/+vU0NVL+QQCDPDHr22ygg=
+github.com/bnb-chain/greenfield-cosmos-sdk v0.2.3-alpha.3/go.mod h1:xgxstYzesnI8FPGm3wcb9ddwnYNXBIbA3ZJBum+8BuU=
 github.com/bnb-chain/greenfield-cosmos-sdk/api v0.0.0-20230425074444-eb5869b05fe9 h1:6fLpmmI0EZvDTfPvI0zy5dBaaTUboHnEkoC5/p/w8TQ=
 github.com/bnb-chain/greenfield-cosmos-sdk/api v0.0.0-20230425074444-eb5869b05fe9/go.mod h1:rbc4o84RSEvhf09o2+4Qiazsv0snRJLiEZdk17HeIDw=
 github.com/bnb-chain/greenfield-cosmos-sdk/math v0.0.0-20230425074444-eb5869b05fe9 h1:1ZdK+iR1Up02bOa2YTZCml7PBpP//kcdamOcK6aWO/s=
 github.com/bnb-chain/greenfield-cosmos-sdk/math v0.0.0-20230425074444-eb5869b05fe9/go.mod h1:Ygz4wBHrgc7g0N+8+MrnTfS9LLn9aaTGa9hKopuym5k=
+github.com/bnb-chain/greenfield-iavl v0.20.1-alpha.1 h1:ZnIcvkkQVurg0OaAwmUGn2cK5bZbffjVChFyhh86HMk=
+github.com/bnb-chain/greenfield-iavl v0.20.1-alpha.1/go.mod h1:oLksTs8dfh7DYIKBro7hbRQ+ewls7ghJ27pIXlbEXyI=
 github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
 github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
 github.com/bradfitz/gomemcache v0.0.0-20170208213004-1952afaa557d/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
@@ -236,8 +240,6 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:z
 github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
 github.com/coinbase/rosetta-sdk-go/types v1.0.0 h1:jpVIwLcPoOeCR6o1tU+Xv7r5bMONNbHU7MuEHboiFuA=
 github.com/coinbase/rosetta-sdk-go/types v1.0.0/go.mod h1:eq7W2TMRH22GTW0N0beDnN931DW0/WOI1R2sdHNHG4c=
-github.com/cometbft/cometbft-db v0.7.0 h1:uBjbrBx4QzU0zOEnU8KxoDl18dMNgDh+zZRUE0ucsbo=
-github.com/cometbft/cometbft-db v0.7.0/go.mod h1:yiKJIm2WKrt6x8Cyxtq9YTEcIMPcEe4XPxhgX59Fzf0=
 github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ=
 github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q=
 github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg=
@@ -264,8 +266,6 @@ github.com/cosmos/gogogateway v1.2.0/go.mod h1:iQpLkGWxYcnCdz5iAdLcRBSw3h7NXeOkZ
 github.com/cosmos/gogoproto v1.4.2/go.mod h1:cLxOsn1ljAHSV527CHOtaIP91kK6cCrZETRBrkzItWU=
 github.com/cosmos/gogoproto v1.4.10 h1:QH/yT8X+c0F4ZDacDv3z+xE3WU1P1Z3wQoLMBRJoKuI=
 github.com/cosmos/gogoproto v1.4.10/go.mod h1:3aAZzeRWpAwr+SS/LLkICX2/kDFyaYVzckBDzygIxek=
-github.com/cosmos/iavl v0.20.0 h1:fTVznVlepH0KK8NyKq8w+U7c2L6jofa27aFX6YGlm38=
-github.com/cosmos/iavl v0.20.0/go.mod h1:WO7FyvaZJoH65+HFOsDir7xU9FWk2w9cHXNW1XHcl7A=
 github.com/cosmos/ledger-cosmos-go v0.13.0 h1:ex0CvCxToSR7j5WjrghPu2Bu9sSXKikjnVvUryNnx4s=
 github.com/cosmos/ledger-cosmos-go v0.13.0/go.mod h1:ZcqYgnfNJ6lAXe4HPtWgarNEY+B74i+2/8MhZw4ziiI=
 github.com/cosmos/rosetta-sdk-go v0.10.0 h1:E5RhTruuoA7KTIXUcMicL76cffyeoyvNybzUGSKFTcM=
@@ -354,9 +354,6 @@ github.com/ethereum/go-ethereum v1.10.13/go.mod h1:W3yfrFyL9C1pHcwY5hmRHVDaorTiQ
 github.com/ethereum/go-ethereum v1.10.22 h1:HbEgsDo1YTGIf4KB/NNpn+XH+PiNJXUZ9ksRxiqWyMc=
 github.com/ethereum/go-ethereum v1.10.22/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg=
 github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
-github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0=
-github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
-github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk=
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
 github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
@@ -912,6 +909,8 @@ github.com/libp2p/go-yamux/v2 v2.3.0/go.mod h1:iTU+lOIn/2h0AgKcL49clNTwfEw+WSfDY
 github.com/libp2p/zeroconf/v2 v2.1.1/go.mod h1:fuJqLnUwZTshS3U/bMRJ3+ow/v9oid1n0DmyYyNO1Xs=
 github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
 github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
+github.com/linxGnu/grocksdb v1.7.16 h1:Q2co1xrpdkr5Hx3Fp+f+f7fRGhQFQhvi/+226dtLmA8=
+github.com/linxGnu/grocksdb v1.7.16/go.mod h1:JkS7pl5qWpGpuVb3bPqTz8nC12X3YtPZT+Xq7+QfQo4=
 github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
 github.com/lucas-clemente/quic-go v0.23.0/go.mod h1:paZuzjXCE5mj6sikVLMvqXk8lJV2AsqtJ6bDhjEfxx0=
 github.com/lucas-clemente/quic-go v0.24.0/go.mod h1:paZuzjXCE5mj6sikVLMvqXk8lJV2AsqtJ6bDhjEfxx0=
@@ -1288,8 +1287,8 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
 github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
-github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
-github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
+github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY=
@@ -1350,8 +1349,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
-github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
 github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
 github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
 github.com/supranational/blst v0.3.5/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
@@ -1360,8 +1359,6 @@ github.com/supranational/blst v0.3.8-0.20220526154634-513d2456b344/go.mod h1:jZJ
 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
 github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
-github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok=
-github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8=
 github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU=
 github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4=
 github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E=
@@ -1511,8 +1508,8 @@ golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5
 golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
-golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
+golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
+golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
 golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1557,7 +1554,7 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
-golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
+golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
 golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1622,8 +1619,8 @@ golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qx
 golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
-golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
+golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
+golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
 golang.org/x/oauth2 v0.0.0-20170912212905-13449ad91cb2/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -1758,13 +1755,13 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
-golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
+golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
-golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
+golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols=
+golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
 golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1851,7 +1848,7 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f
 golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
 golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
-golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
+golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1948,8 +1945,8 @@ google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6D
 google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20210426193834-eac7f76ac494/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
 google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
-google.golang.org/genproto v0.0.0-20230320184635-7606e756e683 h1:khxVcsk/FhnzxMKOyD+TDGwjbEOpcPuIpmafPGFmhMA=
-google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s=
+google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
+google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
 google.golang.org/grpc v1.2.1-0.20170921194603-d4b75ebd4f9f/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
 google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
 google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
@@ -1982,8 +1979,8 @@ google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
 google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
 google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
 google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
-google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag=
-google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
+google.golang.org/grpc v1.56.1 h1:z0dNfjIl0VpaZ9iSVjA6daGatAYwPGstTjt5vkRMFkQ=
+google.golang.org/grpc v1.56.1/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.0.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
diff --git a/internal/sequence/sequence.go b/internal/sequence/sequence.go
new file mode 100644
index 000000000..7cd406e58
--- /dev/null
+++ b/internal/sequence/sequence.go
@@ -0,0 +1,124 @@
+package sequence
+
+import (
+	"encoding/binary"
+
+	"cosmossdk.io/errors"
+	"cosmossdk.io/math"
+	storetypes "github.com/cosmos/cosmos-sdk/store/types"
+)
+
+var (
+	ErrSequenceUniqueConstraint = errors.Register("sequence_u256", 1, "sequence already initialized")
+)
+
+type Number interface {
+	uint32 | math.Uint
+}
+
+type Sequence[T Number] struct {
+	storeKey []byte
+}
+
+func NewSequence[T Number](prefix []byte) Sequence[T] {
+	return Sequence[T]{
+		storeKey: prefix,
+	}
+}
+
+func (s Sequence[T]) NextVal(store storetypes.KVStore) T {
+	v := store.Get(s.storeKey)
+	seq := s.DecodeSequence(v)
+	seq = s.IncreaseSequence(seq)
+	store.Set(s.storeKey, s.EncodeSequence(seq))
+	return any(seq).(T)
+}
+
+// CurVal returns the last value used. 0 if none.
+func (s Sequence[T]) CurVal(store storetypes.KVStore) T {
+	v := store.Get(s.storeKey)
+	ret := s.DecodeSequence(v)
+	return any(ret).(T)
+}
+
+// InitVal this function sets the starting value for a sequence and can only be called once
+// on an empty database. If the key already exists, an error will be returned. The provided
+// start value will be stored as the current value. It is advised to use this function only
+// when the sequence start value is not '1', as calling it unnecessarily will consume
+// unnecessary gas. An example scenario would be importing from genesis.
+// WARNING: only for test now
+func (s Sequence[T]) InitVal(store storetypes.KVStore, seq T) error {
+	if store.Has(s.storeKey) {
+		return ErrSequenceUniqueConstraint
+	}
+	store.Set(s.storeKey, s.EncodeSequence(seq))
+	return nil
+}
+
+func (s Sequence[T]) ToUint32(seq T) uint32 {
+	var t T
+	switch ret := any(t).(type) {
+	case uint32:
+		return ret
+	default:
+		return 0
+	}
+}
+
+func (s Sequence[T]) ToUint256(seq T) math.Uint {
+	var t T
+	switch ret := any(t).(type) {
+	case math.Uint:
+		return ret
+	default:
+		return math.ZeroUint()
+	}
+}
+
+const EncodedSeqLength = 4
+
+func (s Sequence[T]) EncodeSequence(t T) []byte {
+	switch ret := any(t).(type) {
+	case math.Uint:
+		return ret.Bytes()
+	case uint32:
+		bz := make([]byte, EncodedSeqLength)
+		binary.BigEndian.PutUint32(bz, ret)
+		return bz
+	default:
+		return nil
+	}
+}
+
+func (s Sequence[T]) DecodeSequence(bz []byte) T {
+	var t T
+	switch any(t).(type) {
+	case math.Uint:
+		u := math.ZeroUint()
+		if bz != nil {
+			u = u.SetBytes(bz)
+		}
+		return any(u).(T)
+	case uint32:
+		u := uint32(0)
+		if bz != nil {
+			u = binary.BigEndian.Uint32(bz)
+		}
+		return any(u).(T)
+	default:
+		return t
+	}
+}
+
+func (s Sequence[T]) IncreaseSequence(t T) T {
+	switch ret := any(t).(type) {
+	case math.Uint:
+		ret = ret.Incr()
+		return any(ret).(T)
+	case uint32:
+		ret++
+		return any(ret).(T)
+	default:
+		return t
+	}
+}
diff --git a/internal/sequence/sequence_u256_test.go b/internal/sequence/sequence_test.go
similarity index 53%
rename from internal/sequence/sequence_u256_test.go
rename to internal/sequence/sequence_test.go
index a8baa5683..ea59bf579 100644
--- a/internal/sequence/sequence_u256_test.go
+++ b/internal/sequence/sequence_test.go
@@ -42,18 +42,18 @@ func TestSequenceUniqueConstraint(t *testing.T) {
 	ctx := NewMockContext()
 	store := ctx.KVStore(storetypes.NewKVStoreKey("test"))
 
-	seq := sequence.NewSequence256([]byte{0x1})
-	err := seq.InitVal(store, math.NewUint(0))
+	seq := sequence.NewSequence[uint32]([]byte{0x1})
+	err := seq.InitVal(store, 0)
 	require.NoError(t, err)
-	err = seq.InitVal(store, math.NewUint(1))
+	err = seq.InitVal(store, 1)
 	require.True(t, sequence.ErrSequenceUniqueConstraint.Is(err))
 }
 
-func TestSequenceIncrements(t *testing.T) {
+func TestSequenceIncrementsUint256(t *testing.T) {
 	ctx := NewMockContext()
 	store := ctx.KVStore(storetypes.NewKVStoreKey("test"))
-	seq := sequence.NewSequence256([]byte{0x1})
-	max := math.NewUint(10)
+	seq := sequence.NewSequence[math.Uint]([]byte{0x1})
+	max := math.NewUint(1000)
 	i := math.ZeroUint()
 	for i.LT(max) {
 		id := seq.NextVal(store)
@@ -61,6 +61,45 @@ func TestSequenceIncrements(t *testing.T) {
 		i = i.Incr()
 		assert.True(t, i.Equal(id))
 		assert.True(t, i.Equal(curId))
+		fmt.Printf("bytes len %d\n", len(id.Bytes()))
 		fmt.Print("i= ", i.Uint64(), "id=", id.Uint64(), "curID", curId.Uint64())
 	}
 }
+
+func TestSequenceIncrementsU32(t *testing.T) {
+	ctx := NewMockContext()
+	store := ctx.KVStore(storetypes.NewKVStoreKey("test"))
+	seq := sequence.NewSequence[uint32]([]byte{0x1})
+	max := uint32(10)
+	i := uint32(0)
+	for i < max {
+		id := seq.NextVal(store)
+		curId := seq.CurVal(store)
+		i++
+		assert.Equal(t, i, id)
+		assert.Equal(t, i, curId)
+		fmt.Print("i= ", i, "id=", id, "curID", curId)
+	}
+}
+
+func TestSequenceU32(t *testing.T) {
+	ctx := NewMockContext()
+	store := ctx.KVStore(storetypes.NewKVStoreKey("test"))
+
+	seq := sequence.NewSequence[uint32]([]byte{0x1})
+	err := seq.InitVal(store, 0)
+	require.NoError(t, err)
+	n := seq.NextVal(store)
+	require.Equal(t, n, uint32(1))
+}
+
+func TestSequenceU256(t *testing.T) {
+	ctx := NewMockContext()
+	store := ctx.KVStore(storetypes.NewKVStoreKey("test"))
+
+	seq := sequence.NewSequence[math.Uint]([]byte{0x1})
+	err := seq.InitVal(store, math.ZeroUint())
+	require.NoError(t, err)
+	n := seq.NextVal(store)
+	require.Equal(t, n, math.OneUint())
+}
diff --git a/internal/sequence/sequence_u256.go b/internal/sequence/sequence_u256.go
deleted file mode 100644
index f921a964c..000000000
--- a/internal/sequence/sequence_u256.go
+++ /dev/null
@@ -1,68 +0,0 @@
-package sequence
-
-import (
-	"cosmossdk.io/errors"
-	"cosmossdk.io/math"
-	storetypes "github.com/cosmos/cosmos-sdk/store/types"
-)
-
-var (
-	ErrSequenceUniqueConstraint = errors.Register("sequence_u256", 1, "sequence already initialized")
-)
-
-// U256 is a persistent unique key generator based on a counter.
-type U256 struct {
-	storeKey []byte
-}
-
-func NewSequence256(prefix []byte) U256 {
-	return U256{
-		storeKey: prefix,
-	}
-}
-
-// NextVal increments and persists the counter by one and returns the value.
-func (s U256) NextVal(store storetypes.KVStore) math.Uint {
-	v := store.Get(s.storeKey)
-	seq := DecodeSequence(v)
-	seq = seq.Incr()
-	store.Set(s.storeKey, EncodeSequence(seq))
-	return seq
-}
-
-// CurVal returns the last value used. 0 if none.
-func (s U256) CurVal(store storetypes.KVStore) math.Uint {
-	v := store.Get(s.storeKey)
-	return DecodeSequence(v)
-}
-
-// PeekNextVal returns the CurVal + increment step. Not persistent.
-func (s U256) PeekNextVal(store storetypes.KVStore) math.Uint {
-	v := store.Get(s.storeKey)
-	seq := DecodeSequence(v)
-	seq = seq.Incr()
-	return seq
-}
-
-// InitVal this function sets the starting value for a sequence and can only be called once
-// on an empty database. If the key already exists, an error will be returned. The provided
-// start value will be stored as the current value. It is advised to use this function only
-// when the sequence start value is not '1', as calling it unnecessarily will consume
-// unnecessary gas. An example scenario would be importing from genesis.
-func (s U256) InitVal(store storetypes.KVStore, seq math.Uint) error {
-	if store.Has(s.storeKey) {
-		return ErrSequenceUniqueConstraint
-	}
-
-	store.Set(s.storeKey, EncodeSequence(seq))
-	return nil
-}
-
-func EncodeSequence(u math.Uint) []byte {
-	return u.Bytes()
-}
-
-func DecodeSequence(bz []byte) math.Uint {
-	u := math.NewUint(0)
-	return u.SetBytes(bz)
-}
diff --git a/proto/greenfield/bridge/event.proto b/proto/greenfield/bridge/event.proto
index 64c946876..e6031cab0 100644
--- a/proto/greenfield/bridge/event.proto
+++ b/proto/greenfield/bridge/event.proto
@@ -28,6 +28,8 @@ message EventCrossTransferOut {
   cosmos.base.v1beta1.Coin relayer_fee = 4;
   // Sequence of the corresponding cross chain package
   uint64 sequence = 5;
+  // Destination chain id of the cross chain transfer tx
+  uint32 dest_chain_id = 6;
 }
 
 // EventCrossTransferOutRefund is emitted when a cross chain transfer out tx failed
@@ -40,6 +42,8 @@ message EventCrossTransferOutRefund {
   RefundReason refund_reason = 3;
   // Sequence of the corresponding cross chain package
   uint64 sequence = 4;
+  // Destination chain id of the cross chain transfer tx
+  uint32 dest_chain_id = 5;
 }
 
 // EventCrossTransferIn is emitted when a cross chain transfer in tx happened
@@ -52,4 +56,6 @@ message EventCrossTransferIn {
   string refund_address = 3;
   // Sequence of the corresponding cross chain package
   uint64 sequence = 4;
+  // Source chain id of the cross chain transfer tx
+  uint32 src_chain_id = 5;
 }
diff --git a/proto/greenfield/bridge/params.proto b/proto/greenfield/bridge/params.proto
index 697e9fcc5..c3c69f27e 100644
--- a/proto/greenfield/bridge/params.proto
+++ b/proto/greenfield/bridge/params.proto
@@ -8,14 +8,14 @@ option go_package = "github.com/bnb-chain/greenfield/x/bridge/types";
 
 // Params defines the parameters for the module.
 message Params {
-  // Relayer fee for the cross chain transfer out tx
-  string transfer_out_relayer_fee = 1 [
+  // Relayer fee for the cross chain transfer out tx to bsc
+  string bsc_transfer_out_relayer_fee = 1 [
     (cosmos_proto.scalar) = "cosmos.Int",
     (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
     (gogoproto.nullable) = false
   ];
-  // Relayer fee for the ACK or FAIL_ACK package of the cross chain transfer out tx
-  string transfer_out_ack_relayer_fee = 2 [
+  // Relayer fee for the ACK or FAIL_ACK package of the cross chain transfer out tx to bsc
+  string bsc_transfer_out_ack_relayer_fee = 2 [
     (cosmos_proto.scalar) = "cosmos.Int",
     (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
     (gogoproto.nullable) = false
diff --git a/proto/greenfield/challenge/events.proto b/proto/greenfield/challenge/events.proto
index 5540bcc8a..984533d38 100644
--- a/proto/greenfield/challenge/events.proto
+++ b/proto/greenfield/challenge/events.proto
@@ -23,16 +23,19 @@ message EventStartChallenge {
   uint32 segment_index = 3;
 
   // The storage provider to be challenged.
-  string sp_operator_address = 4 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  uint32 sp_id = 4;
+
+  // The storage provider to be challenged.
+  string sp_operator_address = 5 [(cosmos_proto.scalar) = "cosmos.AddressString"];
 
   // The redundancy index, which comes from the index of storage providers.
-  int32 redundancy_index = 5;
+  int32 redundancy_index = 6;
 
   // The challenger who submits the challenge.
-  string challenger_address = 6 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  string challenger_address = 7 [(cosmos_proto.scalar) = "cosmos.AddressString"];
 
   // The challenge will be expired after this height
-  uint64 expired_height = 7;
+  uint64 expired_height = 8;
 }
 
 // EventAttestChallenge to indicate a challenge has been attested.
@@ -44,7 +47,7 @@ message EventAttestChallenge {
   VoteResult result = 2;
 
   // The slashed storage provider address.
-  string sp_operator_address = 3 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  uint32 sp_id = 3;
 
   // The slashed amount from the storage provider.
   string slash_amount = 4;
diff --git a/proto/greenfield/challenge/types.proto b/proto/greenfield/challenge/types.proto
index 39126ca23..057493ab1 100644
--- a/proto/greenfield/challenge/types.proto
+++ b/proto/greenfield/challenge/types.proto
@@ -20,7 +20,7 @@ enum VoteResult {
 // Slash records the storage provider slashes, which will be pruned periodically.
 message Slash {
   // The slashed storage provider.
-  bytes sp_operator_address = 1;
+  uint32 sp_id = 1;
 
   // The slashed object info.
   string object_id = 2 [
diff --git a/proto/greenfield/common/approval.proto b/proto/greenfield/common/approval.proto
new file mode 100644
index 000000000..d9762a59f
--- /dev/null
+++ b/proto/greenfield/common/approval.proto
@@ -0,0 +1,21 @@
+syntax = "proto3";
+
+package greenfield.common;
+
+import "cosmos_proto/cosmos.proto";
+import "gogoproto/gogo.proto";
+import "google/protobuf/any.proto";
+
+option go_package = "github.com/bnb-chain/greenfield/types/common";
+
+// Approval is the signature information returned by the Primary Storage Provider (SP) to the user
+// after allowing them to create a bucket or object, which is then used for verification on the chain
+// to ensure agreement between the Primary SP and the user.
+message Approval {
+  // expired_height is the block height at which the signature expires.
+  uint64 expired_height = 1;
+  // global_virtual_group_family_id is the family id that stored.
+  uint32 global_virtual_group_family_id = 2;
+  // The signature needs to conform to the EIP 712 specification.
+  bytes sig = 3;
+}
diff --git a/proto/greenfield/payment/auto_resume_record.proto b/proto/greenfield/payment/auto_resume_record.proto
new file mode 100644
index 000000000..bd69b04a3
--- /dev/null
+++ b/proto/greenfield/payment/auto_resume_record.proto
@@ -0,0 +1,16 @@
+syntax = "proto3";
+package greenfield.payment;
+
+import "cosmos_proto/cosmos.proto";
+
+option go_package = "github.com/bnb-chain/greenfield/x/payment/types";
+
+// AutoResumeRecord is the record keeps the auto resume information.
+// The EndBlocker of payment module will scan the list of AutoResumeRecord
+// and resume the stream account one by one.
+message AutoResumeRecord {
+  // timestamp is the unix timestamp to order the records
+  int64 timestamp = 1;
+  // the stream account address
+  string addr = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+}
diff --git a/proto/greenfield/payment/events.proto b/proto/greenfield/payment/events.proto
index aec34cfb3..414eaa1d9 100644
--- a/proto/greenfield/payment/events.proto
+++ b/proto/greenfield/payment/events.proto
@@ -3,7 +3,7 @@ package greenfield.payment;
 
 import "cosmos_proto/cosmos.proto";
 import "gogoproto/gogo.proto";
-import "greenfield/payment/base.proto";
+import "greenfield/payment/out_flow.proto";
 import "greenfield/payment/stream_record.proto";
 
 option go_package = "github.com/bnb-chain/greenfield/x/payment/types";
@@ -30,31 +30,35 @@ message EventStreamRecordUpdate {
     (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
     (gogoproto.nullable) = false
   ];
+  // The frozen netflow rate, which is used when resuming stream account
+  string frozen_netflow_rate = 4 [
+    (cosmos_proto.scalar) = "cosmos.Int",
+    (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
+    (gogoproto.nullable) = false
+  ];
   // The balance of the stream account at the latest CRUD timestamp.
-  string static_balance = 4 [
+  string static_balance = 5 [
     (cosmos_proto.scalar) = "cosmos.Int",
     (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
     (gogoproto.nullable) = false
   ];
   // reserved balance of the stream account
   // If the netflow rate is negative, the reserved balance is `netflow_rate * reserve_time`
-  string buffer_balance = 5 [
+  string buffer_balance = 6 [
     (cosmos_proto.scalar) = "cosmos.Int",
     (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
     (gogoproto.nullable) = false
   ];
   // the locked balance of the stream account after it puts a new object and before the object is sealed
-  string lock_balance = 6 [
+  string lock_balance = 7 [
     (cosmos_proto.scalar) = "cosmos.Int",
     (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
     (gogoproto.nullable) = false
   ];
   // the status of the stream account
-  StreamAccountStatus status = 7;
+  StreamAccountStatus status = 8;
   // the unix timestamp when the stream account will be settled
-  int64 settle_timestamp = 8;
-  // the accumulated outflow rates of the stream account
-  repeated OutFlow out_flows = 9 [(gogoproto.nullable) = false];
+  int64 settle_timestamp = 9;
 }
 
 // EventForceSettle may be emitted on all Msgs and EndBlocker when a payment account's
diff --git a/proto/greenfield/payment/base.proto b/proto/greenfield/payment/out_flow.proto
similarity index 60%
rename from proto/greenfield/payment/base.proto
rename to proto/greenfield/payment/out_flow.proto
index 6af04b4c6..c45a4616b 100644
--- a/proto/greenfield/payment/base.proto
+++ b/proto/greenfield/payment/out_flow.proto
@@ -6,6 +6,16 @@ import "gogoproto/gogo.proto";
 
 option go_package = "github.com/bnb-chain/greenfield/x/payment/types";
 
+// OutFlowStatus defines the status of a out flow
+enum OutFlowStatus {
+  option (gogoproto.goproto_enum_prefix) = false;
+
+  // OUT_FLOW_STATUS_ACTIVE defines the active status of a out flow.
+  OUT_FLOW_STATUS_ACTIVE = 0;
+  // OUT_FLOW_STATUS_FROZEN defines the frozen status of a out flow.
+  OUT_FLOW_STATUS_FROZEN = 1;
+}
+
 // OutFlow defines the accumulative outflow stream rate in BNB
 // from a stream account to a Storage Provider
 message OutFlow {
@@ -17,16 +27,6 @@ message OutFlow {
     (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
     (gogoproto.nullable) = false
   ];
-}
-
-// StreamAccountStatus defines the status of a stream account
-enum StreamAccountStatus {
-  option (gogoproto.goproto_enum_prefix) = false;
-
-  // STREAM_ACCOUNT_STATUS_ACTIVE defines the active status of a stream account.
-  STREAM_ACCOUNT_STATUS_ACTIVE = 0;
-  // STREAM_ACCOUNT_STATUS_FROZEN defines the frozen status of a stream account.
-  // A frozen stream account cannot be used as payment address for buckets.
-  // It can be unfrozen by depositing more BNB to the stream account.
-  STREAM_ACCOUNT_STATUS_FROZEN = 1;
+  // status
+  OutFlowStatus status = 3;
 }
diff --git a/proto/greenfield/payment/params.proto b/proto/greenfield/payment/params.proto
index 9392301fc..cafc4dbf2 100644
--- a/proto/greenfield/payment/params.proto
+++ b/proto/greenfield/payment/params.proto
@@ -14,10 +14,12 @@ message Params {
   // Time duration threshold of forced settlement.
   // If dynamic balance is less than NetOutFlowRate * forcedSettleTime, the account can be forced settled.
   uint64 forced_settle_time = 3 [(gogoproto.moretags) = "yaml:\"forced_settle_time\""];
-  // the maximum number of accounts that will be forced settled in one block
-  uint64 max_auto_force_settle_num = 4 [(gogoproto.moretags) = "yaml:\"max_auto_force_settle_num\""];
+  // the maximum number of flows that will be auto forced settled in one block
+  uint64 max_auto_settle_flow_count = 4 [(gogoproto.moretags) = "yaml:\"max_auto_settle_flow_count\""];
+  // the maximum number of flows that will be auto resumed in one block
+  uint64 max_auto_resume_flow_count = 5 [(gogoproto.moretags) = "yaml:\"max_auto_resume_flow_count\""];
   // The denom of fee charged in payment module
-  string fee_denom = 5 [(gogoproto.moretags) = "yaml:\"fee_denom\""];
+  string fee_denom = 6 [(gogoproto.moretags) = "yaml:\"fee_denom\""];
 }
 
 // VersionedParams defines the parameters with multiple versions, each version is stored with different timestamp.
diff --git a/proto/greenfield/payment/query.proto b/proto/greenfield/payment/query.proto
index 73028a67b..9df978904 100644
--- a/proto/greenfield/payment/query.proto
+++ b/proto/greenfield/payment/query.proto
@@ -7,6 +7,7 @@ import "cosmos_proto/cosmos.proto";
 import "gogoproto/gogo.proto";
 import "google/api/annotations.proto";
 import "greenfield/payment/auto_settle_record.proto";
+import "greenfield/payment/out_flow.proto";
 import "greenfield/payment/params.proto";
 import "greenfield/payment/payment_account.proto";
 import "greenfield/payment/payment_account_count.proto";
@@ -28,6 +29,11 @@ service Query {
     option (google.api.http).get = "/greenfield/payment/params/{timestamp}";
   }
 
+  // Queries a StreamRecord by index.
+  rpc OutFlows(QueryOutFlowsRequest) returns (QueryOutFlowsResponse) {
+    option (google.api.http).get = "/greenfield/payment/out_flows/{account}";
+  }
+
   // Queries a StreamRecord by index.
   rpc StreamRecord(QueryGetStreamRecordRequest) returns (QueryGetStreamRecordResponse) {
     option (google.api.http).get = "/greenfield/payment/stream_record/{account}";
@@ -95,6 +101,14 @@ message QueryParamsByTimestampResponse {
   Params params = 1 [(gogoproto.nullable) = false];
 }
 
+message QueryOutFlowsRequest {
+  string account = 1;
+}
+
+message QueryOutFlowsResponse {
+  repeated OutFlow out_flows = 1 [(gogoproto.nullable) = false];
+}
+
 message QueryGetStreamRecordRequest {
   string account = 1;
 }
diff --git a/proto/greenfield/payment/stream_record.proto b/proto/greenfield/payment/stream_record.proto
index ea93f7014..6d54102fa 100644
--- a/proto/greenfield/payment/stream_record.proto
+++ b/proto/greenfield/payment/stream_record.proto
@@ -3,10 +3,21 @@ package greenfield.payment;
 
 import "cosmos_proto/cosmos.proto";
 import "gogoproto/gogo.proto";
-import "greenfield/payment/base.proto";
 
 option go_package = "github.com/bnb-chain/greenfield/x/payment/types";
 
+// StreamAccountStatus defines the status of a stream account
+enum StreamAccountStatus {
+  option (gogoproto.goproto_enum_prefix) = false;
+
+  // STREAM_ACCOUNT_STATUS_ACTIVE defines the active status of a stream account.
+  STREAM_ACCOUNT_STATUS_ACTIVE = 0;
+  // STREAM_ACCOUNT_STATUS_FROZEN defines the frozen status of a stream account.
+  // A frozen stream account cannot be used as payment address for buckets.
+  // It can be unfrozen by depositing more BNB to the stream account.
+  STREAM_ACCOUNT_STATUS_FROZEN = 1;
+}
+
 // Stream Payment Record of a stream account
 message StreamRecord {
   // account address
@@ -43,6 +54,12 @@ message StreamRecord {
   StreamAccountStatus status = 7;
   // the unix timestamp when the stream account will be settled
   int64 settle_timestamp = 8;
-  // the accumulated outflow rates of the stream account
-  repeated OutFlow out_flows = 9 [(gogoproto.nullable) = false];
+  // the count of its out flows
+  uint64 out_flow_count = 9;
+  // the frozen netflow rate, which is used when resuming stream account
+  string frozen_netflow_rate = 10 [
+    (cosmos_proto.scalar) = "cosmos.Int",
+    (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
+    (gogoproto.nullable) = false
+  ];
 }
diff --git a/proto/greenfield/sp/events.proto b/proto/greenfield/sp/events.proto
index 70dd33abf..86b07975a 100644
--- a/proto/greenfield/sp/events.proto
+++ b/proto/greenfield/sp/events.proto
@@ -10,40 +10,48 @@ option go_package = "github.com/bnb-chain/greenfield/x/sp/types";
 
 // EventCreateStorageProvider is emitted when there is a storage provider created
 message EventCreateStorageProvider {
+  // sp_id defines the identifier of storage provider which generated on-chain
+  uint32 sp_id = 1;
   // sp_address is the operator address of the storage provider
-  string sp_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  string sp_address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
   // funding_address is the funding account address of the storage provider
-  string funding_address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  string funding_address = 3 [(cosmos_proto.scalar) = "cosmos.AddressString"];
   // seal_address is the account address for SealObject Tx
-  string seal_address = 3 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  string seal_address = 4 [(cosmos_proto.scalar) = "cosmos.AddressString"];
   // approval_address is the account address for approve create bucket/object signature
-  string approval_address = 4 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  string approval_address = 5 [(cosmos_proto.scalar) = "cosmos.AddressString"];
   // gc_address defines one of the storage provider's accounts which is used for gc purpose
-  string gc_address = 5 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  string gc_address = 6 [(cosmos_proto.scalar) = "cosmos.AddressString"];
   // endpoint is the domain name address used by SP to provide storage services
-  string endpoint = 6;
+  string endpoint = 7;
   // total_deposit is the token coin that the storage provider deposit to the storage module
-  cosmos.base.v1beta1.Coin total_deposit = 7;
+  cosmos.base.v1beta1.Coin total_deposit = 8;
   // status defines the status of the storage provider
-  Status status = 8;
+  Status status = 9;
   // description defines the description terms for the storage provider
-  Description description = 9 [(gogoproto.nullable) = false];
+  Description description = 10 [(gogoproto.nullable) = false];
+  // bls_key defines the bls pub key owned by storage provider used when sealing object and completing migration
+  string bls_key = 11;
 }
 
 // EventEditStorageProvider is emitted when SP's metadata is edited.
 message EventEditStorageProvider {
+  // sp_id defines the identifier of storage provider which generated on-chain
+  uint32 sp_id = 1;
   // sp_address is the operator address of the storage provider
-  string sp_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  string sp_address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
   // endpoint is the service endpoint of the storage provider
-  string endpoint = 2;
+  string endpoint = 3;
   // description defines the description terms for the storage provider
-  Description description = 3 [(gogoproto.nullable) = false];
+  Description description = 4 [(gogoproto.nullable) = false];
   // seal_address is the account address for SealObject Tx
-  string seal_address = 4 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  string seal_address = 5 [(cosmos_proto.scalar) = "cosmos.AddressString"];
   // approval_address is the account address for approve create bucket/object signature
-  string approval_address = 5 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  string approval_address = 6 [(cosmos_proto.scalar) = "cosmos.AddressString"];
   // gc_address defines one of the storage provider's accounts which is used for gc purpose
-  string gc_address = 6 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  string gc_address = 7 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // bls_key defines the bls pub key owned by storage provider used when sealing object
+  string bls_key = 8;
 }
 
 // EventDeposit is emitted when sp deposit tokens.
@@ -57,8 +65,8 @@ message EventDeposit {
 }
 
 message EventSpStoragePriceUpdate {
-  // sp address
-  string sp_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // sp id
+  uint32 sp_id = 1;
   // update time, in unix timestamp
   int64 update_time_sec = 2;
   // read price, in bnb wei per charge byte
diff --git a/proto/greenfield/sp/query.proto b/proto/greenfield/sp/query.proto
index 7997c3130..10f5f2d22 100644
--- a/proto/greenfield/sp/query.proto
+++ b/proto/greenfield/sp/query.proto
@@ -10,7 +10,6 @@ import "greenfield/sp/params.proto";
 import "greenfield/sp/types.proto";
 
 // this line is used by starport scaffolding # 1
-
 option go_package = "github.com/bnb-chain/greenfield/x/sp/types";
 
 // Query defines the gRPC querier service.
@@ -37,9 +36,14 @@ service Query {
     option (google.api.http).get = "/greenfield/sp/get_secondary_sp_store_price_by_time/{timestamp}";
   }
 
-  // Queries a storage provider with specify address
+  // Queries a storage provider with specify id
   rpc StorageProvider(QueryStorageProviderRequest) returns (QueryStorageProviderResponse) {
-    option (google.api.http).get = "/greenfield/storage_provider/{spAddress}";
+    option (google.api.http).get = "/greenfield/storage_provider/{id}";
+  }
+
+  // Queries a StorageProvider by specify operator address.
+  rpc StorageProviderByOperatorAddress(QueryStorageProviderByOperatorAddressRequest) returns (QueryStorageProviderByOperatorAddressResponse) {
+    option (google.api.http).get = "/greenfield/sp/storage_provider_by_operator_address";
   }
 }
 
@@ -86,9 +90,17 @@ message QueryGetSecondarySpStorePriceByTimeResponse {
 }
 
 message QueryStorageProviderRequest {
-  string spAddress = 1;
+  uint32 id = 1;
 }
 
 message QueryStorageProviderResponse {
   StorageProvider storageProvider = 1;
 }
+
+message QueryStorageProviderByOperatorAddressRequest {
+  string operator_address = 1;
+}
+
+message QueryStorageProviderByOperatorAddressResponse {
+  StorageProvider storageProvider = 1;
+}
diff --git a/proto/greenfield/sp/tx.proto b/proto/greenfield/sp/tx.proto
index ebfffab01..893548819 100644
--- a/proto/greenfield/sp/tx.proto
+++ b/proto/greenfield/sp/tx.proto
@@ -64,6 +64,9 @@ message MsgCreateStorageProvider {
     (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
     (gogoproto.nullable) = false
   ];
+  // bls_key defines the bls pub key of the Storage provider for sealing object
+  string bls_key = 13;
+  string bls_proof = 14;
 }
 
 // MsgCreateStorageProviderResponse defines the Msg/CreateStorageProvider response type.
@@ -85,6 +88,7 @@ message MsgDeposit {
 message MsgDepositResponse {}
 
 // MsgEditStorageProvider defines a SDK message for editing an existing sp.
+// TODO: use sp id to edit the storage provider.
 message MsgEditStorageProvider {
   option (cosmos.msg.v1.signer) = "sp_address";
 
@@ -97,6 +101,9 @@ message MsgEditStorageProvider {
   string approval_address = 5 [(cosmos_proto.scalar) = "cosmos.AddressString"];
   // gc_address defines one of the storage provider's accounts which is used for gc purpose
   string gc_address = 6 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // bls_key defines the bls pub key of the Storage provider for sealing object
+  string bls_key = 7;
+  string bls_proof = 8;
 }
 
 // MsgEditStorageProviderResponse defines the Msg/EditStorageProvider response type.
diff --git a/proto/greenfield/sp/types.proto b/proto/greenfield/sp/types.proto
index e3692aa87..7d780d30c 100644
--- a/proto/greenfield/sp/types.proto
+++ b/proto/greenfield/sp/types.proto
@@ -33,28 +33,32 @@ enum Status {
 
 // StorageProvider defines the meta info of storage provider
 message StorageProvider {
+  // // id is the identifier of the storage provider, used in virtual group
+  uint32 id = 1;
   // operator_address defines the account address of the storage provider's operator; It also is the unique index key of sp.
-  string operator_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  string operator_address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
   // funding_address defines one of the storage provider's accounts which is used to deposit and reward.
-  string funding_address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  string funding_address = 3 [(cosmos_proto.scalar) = "cosmos.AddressString"];
   // seal_address defines one of the storage provider's accounts which is used to SealObject
-  string seal_address = 3 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  string seal_address = 4 [(cosmos_proto.scalar) = "cosmos.AddressString"];
   // approval_address defines one of the storage provider's accounts which is used to approve use's createBucket/createObject request
-  string approval_address = 4 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  string approval_address = 5 [(cosmos_proto.scalar) = "cosmos.AddressString"];
   // gc_address defines one of the storage provider's accounts which is used for gc purpose.
-  string gc_address = 5 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  string gc_address = 6 [(cosmos_proto.scalar) = "cosmos.AddressString"];
   // total_deposit defines the number of tokens deposited by this storage provider for staking.
-  string total_deposit = 6 [
+  string total_deposit = 7 [
     (cosmos_proto.scalar) = "cosmos.Int",
     (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
     (gogoproto.nullable) = false
   ];
   // status defines the current service status of this storage provider
-  Status status = 7;
+  Status status = 8;
   // endpoint define the storage provider's network service address
-  string endpoint = 8;
+  string endpoint = 9;
   // description defines the description terms for the storage provider.
-  Description description = 9 [(gogoproto.nullable) = false];
+  Description description = 10 [(gogoproto.nullable) = false];
+  // bls_key defines the bls pub key of the Storage provider for sealing object and completing migration
+  bytes bls_key = 11;
 }
 
 message RewardInfo {
@@ -64,8 +68,8 @@ message RewardInfo {
 
 // storage price of a specific sp
 message SpStoragePrice {
-  // sp address
-  string sp_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // sp id
+  uint32 sp_id = 1;
   // update time, unix timestamp in seconds
   int64 update_time_sec = 2;
   // read price, in bnb wei per charge byte
diff --git a/proto/greenfield/storage/common.proto b/proto/greenfield/storage/common.proto
index ece5ce980..ec69cb7aa 100644
--- a/proto/greenfield/storage/common.proto
+++ b/proto/greenfield/storage/common.proto
@@ -24,6 +24,7 @@ enum BucketStatus {
 
   BUCKET_STATUS_CREATED = 0;
   BUCKET_STATUS_DISCONTINUED = 1;
+  BUCKET_STATUS_MIGRATING = 2;
 }
 
 // RedundancyType represents the redundancy algorithm type for object data,
@@ -58,27 +59,53 @@ enum VisibilityType {
   VISIBILITY_TYPE_INHERIT = 3;
 }
 
-// Approval is the signature information returned by the Primary Storage Provider (SP) to the user
-// after allowing them to create a bucket or object, which is then used for verification on the chain
-// to ensure agreement between the Primary SP and the user."
-message Approval {
-  // expired_height is the block height at which the signature expires.
-  uint64 expired_height = 1;
-  // The signature needs to conform to the EIP 712 specification.
-  bytes sig = 2;
-}
-
-// SecondarySpSignDoc used to generate seal signature of secondary SP
+// SecondarySpSealObjectSignDoc used to generate seal signature of secondary SP
 // If the secondary SP only signs the checksum to declare the object pieces are saved,
 // it might be reused by the primary SP to fake it's declaration.
 // Then the primary SP can challenge and slash the secondary SP.
 // So the id of the object is needed to prevent this.
-message SecondarySpSignDoc {
-  string sp_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
-  string object_id = 2 [
+message SecondarySpSealObjectSignDoc {
+  string chain_id = 1;
+  uint32 global_virtual_group_id = 2;
+  string object_id = 3 [
+    (cosmos_proto.scalar) = "cosmos.Uint",
+    (gogoproto.customtype) = "Uint",
+    (gogoproto.nullable) = false
+  ];
+  // checksum is the sha256 hash of slice of integrity hash from secondary sps
+  bytes checksum = 4;
+}
+
+message GVGMapping {
+  uint32 src_global_virtual_group_id = 1;
+  uint32 dst_global_virtual_group_id = 2;
+  bytes secondary_sp_bls_signature = 3;
+}
+
+message SecondarySpMigrationBucketSignDoc {
+  string chain_id = 1;
+  uint32 dst_primary_sp_id = 2;
+  uint32 src_global_virtual_group_id = 3;
+  uint32 dst_global_virtual_group_id = 4;
+  string bucket_id = 5 [
     (cosmos_proto.scalar) = "cosmos.Uint",
     (gogoproto.customtype) = "Uint",
     (gogoproto.nullable) = false
   ];
-  bytes checksum = 3;
+}
+
+// Local virtual group(LVG) uniquely associated with a global virtual group.
+// Each bucket maintains a mapping from local virtual group to global virtual group
+// Each local virtual group is associated with a unique virtual payment account,
+// where all object fees are streamed to.
+message LocalVirtualGroup {
+  // id is the identifier of the local virtual group.
+  uint32 id = 1;
+  // global_virtual_group_id is the identifier of the global virtual group.
+  uint32 global_virtual_group_id = 3;
+  // stored_size is the size of the stored data in the local virtual group.
+  uint64 stored_size = 4;
+  // total_charge_size is the total charged size of the objects in the LVG.
+  // Notice that the minimum unit of charge is 128K
+  uint64 total_charge_size = 5;
 }
diff --git a/proto/greenfield/storage/events.proto b/proto/greenfield/storage/events.proto
index 3e1f40f52..d14c1c38c 100644
--- a/proto/greenfield/storage/events.proto
+++ b/proto/greenfield/storage/events.proto
@@ -30,10 +30,12 @@ message EventCreateBucket {
   uint64 charged_read_quota = 7;
   // payment_address is the address of the payment account
   string payment_address = 8 [(cosmos_proto.scalar) = "cosmos.AddressString"];
-  // primary_sp_address is the operator address of the primary sp.
-  string primary_sp_address = 9 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // primary_sp_id is the unique id of primary sp.
+  uint32 primary_sp_id = 9;
+  // global_virtual_group_family_id defines the unique id of gvg family
+  uint32 global_virtual_group_family_id = 10;
   // status define the status of the bucket.
-  BucketStatus status = 10;
+  BucketStatus status = 11;
 }
 
 // EventDeleteBucket is emitted on MsgDeleteBucket
@@ -50,8 +52,8 @@ message EventDeleteBucket {
     (gogoproto.customtype) = "Uint",
     (gogoproto.nullable) = false
   ];
-  // primary_sp_address define the account address of primary sp
-  string primary_sp_address = 5 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // primary_sp_id is the unique id of primary sp.
+  uint32 primary_sp_id = 5;
 }
 
 // EventUpdateBucketInfo is emitted on MsgUpdateBucketInfo
@@ -116,8 +118,8 @@ message EventCreateObject {
     (gogoproto.customtype) = "Uint",
     (gogoproto.nullable) = false
   ];
-  // primary_sp_address define the account address of primary sp
-  string primary_sp_address = 8 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // primary_sp_id define the unique id of primary sp
+  uint32 primary_sp_id = 8;
   // payload_size define the size of payload data which you want upload
   uint64 payload_size = 9;
   // visibility defines the highest permission of object.
@@ -136,7 +138,7 @@ message EventCreateObject {
   repeated bytes checksums = 16;
 }
 
-// EventSealObject is emitted on MsgSealObject
+// EventCancelCreateObject is emitted on MsgCancelCreateObject
 message EventCancelCreateObject {
   // operator define the account address of operator who cancel create object
   string operator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
@@ -144,8 +146,8 @@ message EventCancelCreateObject {
   string bucket_name = 2;
   // object_name define the name of the object
   string object_name = 3;
-  // primary_sp_address define the operator account address of the sp
-  string primary_sp_address = 4 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // primary_sp_id define the unique id of primary sp
+  uint32 primary_sp_id = 4;
   // id define an u256 id for object
   string object_id = 6 [
     (cosmos_proto.scalar) = "cosmos.Uint",
@@ -170,8 +172,10 @@ message EventSealObject {
   ];
   // status define the status of the object. INIT or IN_SERVICE or others
   ObjectStatus status = 6;
-  // secondary_sp_address define all the operator address of the secondary sps
-  repeated string secondary_sp_addresses = 7 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // global_virtual_group_id defines the unique id of gvg which the object stored
+  uint32 global_virtual_group_id = 7;
+  // local_virtual_group_id defines the unique id of lvg which the object stored
+  uint32 local_virtual_group_id = 8;
 }
 
 // EventCopyObject is emitted on MsgCopyObject
@@ -214,10 +218,8 @@ message EventDeleteObject {
     (gogoproto.customtype) = "Uint",
     (gogoproto.nullable) = false
   ];
-  // primary_sp_address define the operator account address of the sp
-  string primary_sp_address = 5 [(cosmos_proto.scalar) = "cosmos.AddressString"];
-  // secondary_sp_address define all the operator address of the secondary sps
-  repeated string secondary_sp_addresses = 6 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // local_virtual_group_id defines the unique id of lvg which the object stored
+  uint32 local_virtual_group_id = 5;
 }
 
 // EventRejectSealObject is emitted on MsgRejectSealObject
@@ -370,6 +372,8 @@ message EventMirrorBucket {
     (gogoproto.customtype) = "Uint",
     (gogoproto.nullable) = false
   ];
+  // chain id of the destination chain
+  uint32 dest_chain_id = 5;
 }
 
 // EventMirrorBucketResult is emitted on receiving ack package from destination chain
@@ -384,6 +388,8 @@ message EventMirrorBucketResult {
     (gogoproto.customtype) = "Uint",
     (gogoproto.nullable) = false
   ];
+  // chain id of the destination chain
+  uint32 dest_chain_id = 5;
 }
 
 // EventMirrorObject is emitted on MirrorObject
@@ -400,6 +406,8 @@ message EventMirrorObject {
     (gogoproto.customtype) = "Uint",
     (gogoproto.nullable) = false
   ];
+  // chain id of the destination chain
+  uint32 dest_chain_id = 5;
 }
 
 // EventMirrorObjectResult is emitted on receiving ack package from destination chain
@@ -416,6 +424,8 @@ message EventMirrorObjectResult {
     (gogoproto.customtype) = "Uint",
     (gogoproto.nullable) = false
   ];
+  // chain id of the destination chain
+  uint32 dest_chain_id = 5;
 }
 
 // EventMirrorGroup is emitted on MirrorGroup
@@ -430,6 +440,8 @@ message EventMirrorGroup {
     (gogoproto.customtype) = "Uint",
     (gogoproto.nullable) = false
   ];
+  // chain id of the destination chain
+  uint32 dest_chain_id = 4;
 }
 
 // EventMirrorGroupResult is emitted on receiving ack package from destination chain
@@ -444,6 +456,8 @@ message EventMirrorGroupResult {
     (gogoproto.customtype) = "Uint",
     (gogoproto.nullable) = false
   ];
+  // chain id of the destination chain
+  uint32 dest_chain_id = 4;
 }
 
 // EventStalePolicyCleanup is emitted when specified block height's stale policies need to be Garbage collected
@@ -451,3 +465,51 @@ message EventStalePolicyCleanup {
   int64 blockNum = 1;
   DeleteInfo delete_info = 2;
 }
+
+message EventMigrationBucket {
+  // The address of the operator that initiated the bucket migration,
+  // usually the owner of the bucket or another account which has permission to operate
+  string operator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // The name of the bucket to be migrated
+  string bucket_name = 2;
+  // bucket_id define an u256 id for object
+  string bucket_id = 3 [
+    (cosmos_proto.scalar) = "cosmos.Uint",
+    (gogoproto.customtype) = "Uint",
+    (gogoproto.nullable) = false
+  ];
+  // The id of the destination primary sp
+  uint32 dst_primary_sp_id = 4;
+}
+
+message EventCancelMigrationBucket {
+  // The address of the operator that canceled the bucket migration,
+  // usually the owner of the bucket or another account which has permission to operate
+  string operator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // The name of the bucket to be migrated
+  string bucket_name = 2;
+  // bucket_id define an u256 id for object
+  string bucket_id = 3 [
+    (cosmos_proto.scalar) = "cosmos.Uint",
+    (gogoproto.customtype) = "Uint",
+    (gogoproto.nullable) = false
+  ];
+}
+
+message EventCompleteMigrationBucket {
+  // The address of the operator that initiated the bucket migration,
+  // usually the owner of the bucket or another account which has permission to operate
+  string operator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // The name of the bucket to be migrated
+  string bucket_name = 2;
+  // bucket_id define an u256 id for object
+  string bucket_id = 3 [
+    (cosmos_proto.scalar) = "cosmos.Uint",
+    (gogoproto.customtype) = "Uint",
+    (gogoproto.nullable) = false
+  ];
+  // The family id that the bucket to be migrated to
+  uint32 global_virtual_group_family_id = 4;
+  // The src and dst gvg mapping
+  repeated GVGMapping gvg_mappings = 5;
+}
diff --git a/proto/greenfield/storage/params.proto b/proto/greenfield/storage/params.proto
index da7e49111..3d84d199f 100644
--- a/proto/greenfield/storage/params.proto
+++ b/proto/greenfield/storage/params.proto
@@ -12,18 +12,18 @@ message Params {
 
   // max_payload_size is the maximum size of the payload, default: 2G
   uint64 max_payload_size = 2;
-  // relayer fee for the mirror bucket tx
-  string mirror_bucket_relayer_fee = 3;
-  // relayer fee for the ACK or FAIL_ACK package of the mirror bucket tx
-  string mirror_bucket_ack_relayer_fee = 4;
-  // relayer fee for the mirror object tx
-  string mirror_object_relayer_fee = 5;
-  // Relayer fee for the ACK or FAIL_ACK package of the mirror object tx
-  string mirror_object_ack_relayer_fee = 6;
-  // relayer fee for the mirror object tx
-  string mirror_group_relayer_fee = 7;
-  // Relayer fee for the ACK or FAIL_ACK package of the mirror object tx
-  string mirror_group_ack_relayer_fee = 8;
+  // relayer fee for the mirror bucket tx to bsc
+  string bsc_mirror_bucket_relayer_fee = 3;
+  // relayer fee for the ACK or FAIL_ACK package of the mirror bucket tx to bsc
+  string bsc_mirror_bucket_ack_relayer_fee = 4;
+  // relayer fee for the mirror object tx to bsc
+  string bsc_mirror_object_relayer_fee = 5;
+  // Relayer fee for the ACK or FAIL_ACK package of the mirror object tx to bsc
+  string bsc_mirror_object_ack_relayer_fee = 6;
+  // relayer fee for the mirror object tx to bsc
+  string bsc_mirror_group_relayer_fee = 7;
+  // Relayer fee for the ACK or FAIL_ACK package of the mirror object tx to bsc
+  string bsc_mirror_group_ack_relayer_fee = 8;
   // The maximum number of buckets that can be created per account
   uint32 max_buckets_per_account = 9;
   // The window to count the discontinued objects or buckets
@@ -40,6 +40,8 @@ message Params {
   uint64 stale_policy_cleanup_max = 15;
   // The min interval for making quota smaller in seconds
   uint64 min_quota_update_interval = 16;
+  // the max number of local virtual group per bucket
+  uint32 max_local_virtual_group_num_per_bucket = 17;
 }
 
 // VersionedParams defines the parameters for the storage module with multi version, each version store with different timestamp.
diff --git a/proto/greenfield/storage/query.proto b/proto/greenfield/storage/query.proto
index 76283c5bb..ad2bb14eb 100644
--- a/proto/greenfield/storage/query.proto
+++ b/proto/greenfield/storage/query.proto
@@ -10,6 +10,7 @@ import "greenfield/permission/common.proto";
 import "greenfield/permission/types.proto";
 import "greenfield/storage/params.proto";
 import "greenfield/storage/types.proto";
+import "greenfield/virtualgroup/types.proto";
 
 // this line is used by starport scaffolding # 1
 
@@ -112,6 +113,11 @@ service Query {
     option (google.api.http).get = "/greenfield/storage/policy_by_id/{policy_id}";
   }
 
+  // Queries lock fee for storing an object
+  rpc QueryLockFee(QueryLockFeeRequest) returns (QueryLockFeeResponse) {
+    option (google.api.http).get = "/greenfield/storage/lock_fee";
+  }
+
   // this line is used by starport scaffolding # 2
 }
 
@@ -159,6 +165,7 @@ message QueryHeadObjectByIdRequest {
 
 message QueryHeadObjectResponse {
   ObjectInfo object_info = 1;
+  virtualgroup.GlobalVirtualGroup global_virtual_group = 2;
 }
 
 message QueryListBucketsRequest {
@@ -267,4 +274,21 @@ message QueryPolicyByIdResponse {
   permission.Policy policy = 1;
 }
 
+message QueryLockFeeRequest {
+  // primary_sp_address is the address of the primary sp.
+  string primary_sp_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // create_at define the block timestamp when the object created.
+  int64 create_at = 2;
+  // payloadSize is the total size of the object payload
+  uint64 payload_size = 3;
+}
+
+message QueryLockFeeResponse {
+  string amount = 1 [
+    (cosmos_proto.scalar) = "cosmos.Int",
+    (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
+    (gogoproto.nullable) = false
+  ];
+}
+
 // this line is used by starport scaffolding # 3
diff --git a/proto/greenfield/storage/tx.proto b/proto/greenfield/storage/tx.proto
index 2b6ed2d27..2537a7b70 100644
--- a/proto/greenfield/storage/tx.proto
+++ b/proto/greenfield/storage/tx.proto
@@ -7,6 +7,7 @@ import "cosmos/msg/v1/msg.proto";
 import "cosmos_proto/cosmos.proto";
 import "gogoproto/gogo.proto";
 import "google/protobuf/timestamp.proto";
+import "greenfield/common/approval.proto";
 import "greenfield/common/wrapper.proto";
 import "greenfield/permission/common.proto";
 import "greenfield/storage/common.proto";
@@ -41,37 +42,48 @@ service Msg {
   rpc UpdateGroupExtra(MsgUpdateGroupExtra) returns (MsgUpdateGroupExtraResponse);
   rpc LeaveGroup(MsgLeaveGroup) returns (MsgLeaveGroupResponse);
   rpc MirrorGroup(MsgMirrorGroup) returns (MsgMirrorGroupResponse);
+
   // basic operation of policy
   rpc PutPolicy(MsgPutPolicy) returns (MsgPutPolicyResponse);
   rpc DeletePolicy(MsgDeletePolicy) returns (MsgDeletePolicyResponse);
 
   // UpdateParams defines a governance operation for updating the x/storage module parameters.
   // The authority is defined in the keeper.
-  //
+
   // Since: cosmos-sdk 0.47
   rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse);
+
   // this line is used by starport scaffolding # proto/tx/rpc
+  rpc MigrateBucket(MsgMigrateBucket) returns (MsgMigrateBucketResponse);
+  rpc CompleteMigrateBucket(MsgCompleteMigrateBucket) returns (MsgCompleteMigrateBucketResponse);
+  rpc CancelMigrateBucket(MsgCancelMigrateBucket) returns (MsgCancelMigrateBucketResponse);
 }
 message MsgCreateBucket {
   option (cosmos.msg.v1.signer) = "creator";
 
   // creator defines the account address of bucket creator, it is also the bucket owner.
   string creator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // bucket_name defines a globally unique name of bucket
   string bucket_name = 2;
+
   // visibility means the bucket is private or public. if private, only bucket owner or grantee can read it,
   // otherwise every greenfield user can read it.
   VisibilityType visibility = 3;
+
   // payment_address defines an account address specified by bucket owner to pay the read fee. Default: creator
   string payment_address = 4 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // primary_sp_address defines the address of primary sp.
-  string primary_sp_address = 6 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  string primary_sp_address = 5 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // primary_sp_approval defines the approval info of the primary SP which indicates that primary sp confirm the user's request.
-  Approval primary_sp_approval = 7;
+  common.Approval primary_sp_approval = 6;
+
   // charged_read_quota defines the read data that users are charged for, measured in bytes.
   // The available read data for each user is the sum of the free read data provided by SP and
   // the ChargeReadQuota specified here.
-  uint64 charged_read_quota = 8;
+  uint64 charged_read_quota = 7;
 }
 
 message MsgCreateBucketResponse {
@@ -87,6 +99,7 @@ message MsgDeleteBucket {
 
   // creator defines the account address of the grantee who has the DeleteBucket permission of the bucket to be deleted.
   string operator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // bucket_name defines the name of the bucket to be deleted.
   string bucket_name = 2;
 }
@@ -98,8 +111,10 @@ message MsgDiscontinueBucket {
 
   // operator is the sp who wants to stop serving the bucket.
   string operator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // bucket_name defines the name of the bucket where the object which to be discontinued is stored.
   string bucket_name = 2;
+
   // the reason for the request.
   string reason = 3;
 }
@@ -111,25 +126,31 @@ message MsgCreateObject {
 
   // creator defines the account address of object uploader
   string creator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // bucket_name defines the name of the bucket where the object is stored.
   string bucket_name = 2;
+
   // object_name defines the name of object
   string object_name = 3;
+
   // payload_size defines size of the object's payload
   uint64 payload_size = 4;
+
   // visibility means the object is private or public. if private, only object owner or grantee can access it,
   // otherwise every greenfield user can access it.
   VisibilityType visibility = 5;
+
   // content_type defines a standard MIME type describing the format of the object.
   string content_type = 6;
+
   // primary_sp_approval defines the approval info of the primary SP which indicates that primary sp confirm the user's request.
-  Approval primary_sp_approval = 7;
+  common.Approval primary_sp_approval = 7;
+
   // expect_checksums defines a list of hashes which was generate by redundancy algorithm.
   repeated bytes expect_checksums = 8;
+
   // redundancy_type can be ec or replica
   RedundancyType redundancy_type = 9;
-  // expect_secondarySPs defines a list of StorageProvider address, which is optional
-  repeated string expect_secondary_sp_addresses = 10 [(cosmos_proto.scalar) = "cosmos.AddressString"];
 }
 
 message MsgCreateObjectResponse {
@@ -145,15 +166,19 @@ message MsgSealObject {
 
   // operator defines the account address of primary SP
   string operator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // bucket_name defines the name of the bucket where the object is stored.
   string bucket_name = 2;
+
   // object_name defines the name of object to be sealed.
   string object_name = 3;
-  // secondary_sp_addresses defines a list of storage provider which store the redundant data.
-  repeated string secondary_sp_addresses = 4 [(cosmos_proto.scalar) = "cosmos.AddressString"];
-  // secondary_sp_signatures defines the signature of the secondary sp that can
+
+  // global_virtual_group_id defines the id of global virtual group
+  uint32 global_virtual_group_id = 4;
+
+  // secondary_sp_bls_agg_signatures defines the aggregate bls signature of the secondary sp that can
   // acknowledge that the payload data has received and stored.
-  repeated bytes secondary_sp_signatures = 5;
+  bytes secondary_sp_bls_agg_signatures = 5;
 }
 
 message MsgSealObjectResponse {}
@@ -163,8 +188,10 @@ message MsgRejectSealObject {
 
   // operator defines the account address of the object owner
   string operator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // bucket_name defines the name of the bucket where the object is stored.
   string bucket_name = 2;
+
   // object_name defines the name of unsealed object to be reject.
   string object_name = 3;
 }
@@ -176,16 +203,21 @@ message MsgCopyObject {
 
   // operator defines the account address of the operator who has the CopyObject permission of the object to be deleted.
   string operator = 1;
+
   // src_bucket_name defines the name of the bucket where the object to be copied is located
   string src_bucket_name = 2;
+
   // dst_bucket_name defines the name of the bucket where the object is copied to.
   string dst_bucket_name = 3;
+
   // src_object_name defines the name of the object which to be copied
   string src_object_name = 4;
+
   // dst_object_name defines the name of the object which is copied to
   string dst_object_name = 5;
+
   // primary_sp_approval defines the approval info of the primary SP which indicates that primary sp confirm the user's request.
-  Approval dst_primary_sp_approval = 6;
+  common.Approval dst_primary_sp_approval = 6;
 }
 
 message MsgCopyObjectResponse {
@@ -201,8 +233,10 @@ message MsgDeleteObject {
 
   // operator defines the account address of the operator who has the DeleteObject permission of the object to be deleted.
   string operator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // bucket_name defines the name of the bucket where the object which to be deleted is stored.
   string bucket_name = 2;
+
   // object_name defines the name of the object which to be deleted.
   string object_name = 3;
 }
@@ -214,14 +248,17 @@ message MsgDiscontinueObject {
 
   // operator is the sp who wants to stop serving the objects.
   string operator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // bucket_name defines the name of the bucket where the object which to be discontinued is stored.
   string bucket_name = 2;
+
   // object_ids are the ids of object info.
   repeated string object_ids = 3 [
     (cosmos_proto.scalar) = "cosmos.Uint",
     (gogoproto.customtype) = "Uint",
     (gogoproto.nullable) = false
   ];
+
   // the reason for the request.
   string reason = 4;
 }
@@ -233,10 +270,13 @@ message MsgCreateGroup {
 
   // owner defines the account address of group owner who create the group
   string creator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // group_name defines the name of the group. it's not globally unique.
   string group_name = 2;
+
   // member_request defines a list of member which to be add or remove
   repeated string members = 3 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // extra defines extra info for the group
   string extra = 4;
 }
@@ -254,6 +294,7 @@ message MsgDeleteGroup {
 
   // operator defines the account address of the operator who has the DeleteGroup permission of the group to be deleted.
   string operator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // group_name defines the name of the group which to be deleted
   string group_name = 2;
 }
@@ -265,12 +306,16 @@ message MsgUpdateGroupMember {
 
   // operator defines the account address of the operator who has the UpdateGroupMember permission of the group.
   string operator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // group_owner defines the account address of the group owner
   string group_owner = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // group_name defines the name of the group which to be updated
   string group_name = 3;
+
   // members_to_add defines a list of members account address which will be add to the group
   repeated string members_to_add = 4 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // members_to_delete defines a list of members account address which will be remove from the group
   repeated string members_to_delete = 5 [(cosmos_proto.scalar) = "cosmos.AddressString"];
 }
@@ -282,10 +327,13 @@ message MsgUpdateGroupExtra {
 
   // operator defines the account address of the operator who has the UpdateGroupMember permission of the group.
   string operator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // group_owner defines the account address of the group owner
   string group_owner = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // group_name defines the name of the group which to be updated
   string group_name = 3;
+
   // extra defines extra info for the group to update
   string extra = 4;
 }
@@ -297,8 +345,10 @@ message MsgLeaveGroup {
 
   // member defines the account address of the member who want to leave the group
   string member = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // group_owner defines the owner of the group you want to leave
   string group_owner = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // group_name defines the name of the group you want to leave
   string group_name = 3;
 }
@@ -310,14 +360,18 @@ message MsgUpdateBucketInfo {
 
   // operator defines the account address of the operator
   string operator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // bucket_name defines the name of bucket which you'll update
   string bucket_name = 2;
+
   // charged_read_quota defines the traffic quota that you read from primary sp
   // if read_quota is nil, it means don't change the read_quota
   common.UInt64Value charged_read_quota = 3;
+
   // payment_address defines the account address of the payment account
   // if payment_address is empty, it means don't change the payment_address
   string payment_address = 4 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // visibility means the bucket is private or public. if private, only bucket owner or grantee can read it,
   // otherwise every greenfield user can read it.
   VisibilityType visibility = 5;
@@ -330,8 +384,10 @@ message MsgCancelCreateObject {
 
   // operator defines the account address of the operator
   string operator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // bucket_name defines the name of the bucket
   string bucket_name = 2;
+
   // object_name defines the name of the object
   string object_name = 3;
 }
@@ -343,12 +399,16 @@ message MsgPutPolicy {
 
   // operator defines the granter who grant the permission to another principal
   string operator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // Principal defines the roles that can grant permissions. Currently, it can be account or group.
   permission.Principal principal = 2;
+
   // resource defines a greenfield standard resource name that can be generated by GRN structure
   string resource = 3;
+
   // statements defines a list of individual statement which describe the detail rules of policy
   repeated permission.Statement statements = 4;
+
   // expiration_time defines the whole expiration time of all the statements.
   // Notices: Its priority is higher than the expiration time inside the Statement
   google.protobuf.Timestamp expiration_time = 7 [
@@ -370,8 +430,10 @@ message MsgDeletePolicy {
 
   // operator defines the granter who grant the permission to another principal
   string operator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // Principal defines the roles that can grant permissions. Currently, it can be account or group.
   permission.Principal principal = 2;
+
   // resource defines a greenfield standard resource name that can be generated by GRN structure
   string resource = 3;
 }
@@ -386,16 +448,20 @@ message MsgDeletePolicyResponse {
 
 message MsgMirrorObject {
   option (cosmos.msg.v1.signer) = "operator";
+
   // operator defines the account address of the operator who has the DeleteObject permission of the object to be deleted.
   string operator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // id defines the unique u256 for object.
   string id = 2 [
     (cosmos_proto.scalar) = "cosmos.Uint",
     (gogoproto.customtype) = "Uint",
     (gogoproto.nullable) = false
   ];
+
   // bucket_name defines the name of the bucket where the object is stored
   string bucket_name = 3;
+
   // object_name defines the name of object
   string object_name = 4;
 }
@@ -404,14 +470,17 @@ message MsgMirrorObjectResponse {}
 
 message MsgMirrorBucket {
   option (cosmos.msg.v1.signer) = "operator";
+
   // creator defines the account address of the grantee who has the DeleteBucket permission of the bucket to be deleted.
   string operator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // id defines the unique u256 for bucket.
   string id = 2 [
     (cosmos_proto.scalar) = "cosmos.Uint",
     (gogoproto.customtype) = "Uint",
     (gogoproto.nullable) = false
   ];
+
   // bucket_name defines a globally unique name of bucket
   string bucket_name = 3;
 }
@@ -423,10 +492,13 @@ message MsgUpdateObjectInfo {
 
   // operator defines the account address of the operator
   string operator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // bucket_name is the name of the bucket
   string bucket_name = 2;
+
   // object_name defines the name of bucket which you'll update
   string object_name = 3;
+
   // visibility means the object is private or public. if private, only bucket owner or grantee can read it,
   // otherwise every greenfield user can read it.
   VisibilityType visibility = 4;
@@ -436,14 +508,17 @@ message MsgMirrorBucketResponse {}
 
 message MsgMirrorGroup {
   option (cosmos.msg.v1.signer) = "operator";
+
   // operator defines the account address of the operator who is the owner of the group
   string operator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+
   // id defines the unique u256 for group.
   string id = 2 [
     (cosmos_proto.scalar) = "cosmos.Uint",
     (gogoproto.customtype) = "Uint",
     (gogoproto.nullable) = false
   ];
+
   // group_name defines the name of the group
   string group_name = 3;
 }
@@ -458,13 +533,53 @@ message MsgUpdateParams {
   string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
 
   // params defines the x/storage parameters to update.
-  //
   // NOTE: All parameters must be supplied.
   Params params = 2 [(gogoproto.nullable) = false];
 }
 
 // MsgUpdateParamsResponse defines the response structure for executing a
-// MsgUpdateParams message.
 message MsgUpdateParamsResponse {}
 
 // this line is used by starport scaffolding # proto/tx/message
+message MsgMigrateBucket {
+  option (cosmos.msg.v1.signer) = "operator";
+
+  // operator defines the account address of the operator who initial the migrate bucket
+  string operator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // bucket_name defines the name of the bucket that need to be migrated
+  string bucket_name = 2;
+  // dst_primary_sp_id defines the destination SP for migration
+  uint32 dst_primary_sp_id = 3;
+  // dst_primary_sp_approval defines the approval of destination sp
+  common.Approval dst_primary_sp_approval = 4;
+}
+
+message MsgMigrateBucketResponse {}
+
+message MsgCompleteMigrateBucket {
+  option (cosmos.msg.v1.signer) = "operator";
+
+  // operator defines the account address of the msg operator.
+  // The CompleteMigrateBucket transaction must be initiated by the destination SP of the migration
+  string operator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // bucket_name defines the name of the bucket that need to be migrated
+  string bucket_name = 2;
+  // global_virtual_group_family_id defines the family id which the bucket migrate to
+  uint32 global_virtual_group_family_id = 3;
+  // gvg_mappings defines the src and dst gvg mapping relationships which the bucket migrate to
+  repeated GVGMapping gvg_mappings = 4;
+}
+
+message MsgCompleteMigrateBucketResponse {}
+
+message MsgCancelMigrateBucket {
+  option (cosmos.msg.v1.signer) = "operator";
+
+  // operator defines the account address of the msg operator.
+  // Only the user can send this transaction to cancel the migrate bucket
+  string operator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // bucket_name defines the name of the bucket that need to be migrated
+  string bucket_name = 2;
+}
+
+message MsgCancelMigrateBucketResponse {}
diff --git a/proto/greenfield/storage/types.proto b/proto/greenfield/storage/types.proto
index 660ab4e0f..0b28518f9 100644
--- a/proto/greenfield/storage/types.proto
+++ b/proto/greenfield/storage/types.proto
@@ -1,11 +1,14 @@
 syntax = "proto3";
 package greenfield.storage;
 
+import "cosmos/base/v1beta1/coin.proto";
 import "cosmos_proto/cosmos.proto";
 import "gogoproto/gogo.proto";
-import "greenfield/payment/base.proto";
+import "greenfield/payment/out_flow.proto";
+import "greenfield/payment/stream_record.proto";
 import "greenfield/storage/common.proto";
 
+// this line is used by starport scaffolding # proto/tx/import
 option go_package = "github.com/bnb-chain/greenfield/x/storage/types";
 
 message BucketInfo {
@@ -27,68 +30,63 @@ message BucketInfo {
   int64 create_at = 6;
   // payment_address is the address of the payment account
   string payment_address = 7 [(cosmos_proto.scalar) = "cosmos.AddressString"];
-  // primary_sp_address is the address of the primary sp. Objects belongs to this bucket will never
+  // primary_sp_id is the unique id of the primary sp. Objects belongs to this bucket will never
   // leave this SP, unless you explicitly shift them to another SP.
-  string primary_sp_address = 8 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  uint32 primary_sp_id = 8;
+  // global_virtual_group_family_id defines the unique id of gvg family
+  uint32 global_virtual_group_family_id = 9;
   // charged_read_quota defines the traffic quota for read in bytes per month.
   // The available read data for each user is the sum of the free read data provided by SP and
   // the ChargeReadQuota specified here.
-  uint64 charged_read_quota = 9;
-  // billing info of the bucket
-  BillingInfo billing_info = 10 [(gogoproto.nullable) = false];
+  uint64 charged_read_quota = 10;
   // bucket_status define the status of the bucket.
   BucketStatus bucket_status = 11;
 }
 
-// BillingInfo is the billing information of the bucket
-message BillingInfo {
+message InternalBucketInfo {
   // the time of the payment price, used to calculate the charge rate of the bucket
   int64 price_time = 1;
   // the total size of the objects in the bucket, used to calculate the charge rate of the bucket
   uint64 total_charge_size = 2;
-  // secondary sp objects size statistics
-  repeated SecondarySpObjectsSize secondary_sp_objects_size = 3 [(gogoproto.nullable) = false];
-}
-
-// secondary sp objects size statistics
-message SecondarySpObjectsSize {
-  // address is the address of the secondary sp
-  string sp_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
-  // size is the total size of the objects in the secondary sp
-  uint64 total_charge_size = 2;
+  // local_virtual_groups contains all the lvg of this bucket.
+  repeated LocalVirtualGroup local_virtual_groups = 3;
+  // next_local_virtual_group_id store the next id used by local virtual group
+  uint32 next_local_virtual_group_id = 4;
 }
 
 message ObjectInfo {
+  // owner is the object owner
   string owner = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // creator is the address of the uploader, it always be same as owner address
+  string creator = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
   // bucket_name is the name of the bucket
-  string bucket_name = 2;
+  string bucket_name = 3;
   // object_name is the name of object
-  string object_name = 3;
+  string object_name = 4;
   // id is the unique identifier of object
-  string id = 4 [
+  string id = 5 [
     (cosmos_proto.scalar) = "cosmos.Uint",
     (gogoproto.customtype) = "Uint",
     (gogoproto.nullable) = false
   ];
+  uint32 local_virtual_group_id = 6;
   // payloadSize is the total size of the object payload
-  uint64 payload_size = 5;
+  uint64 payload_size = 7;
   // visibility defines the highest permissions for object. When an object is public, everyone can access it.
-  VisibilityType visibility = 6;
+  VisibilityType visibility = 8;
   // content_type define the format of the object which should be a standard MIME type.
-  string content_type = 7;
+  string content_type = 9;
   // create_at define the block timestamp when the object is created
-  int64 create_at = 8;
+  int64 create_at = 10;
   // object_status define the upload status of the object.
-  ObjectStatus object_status = 9;
+  ObjectStatus object_status = 11;
   // redundancy_type define the type of the redundancy which can be multi-replication or EC.
-  RedundancyType redundancy_type = 10;
+  RedundancyType redundancy_type = 12;
   // source_type define the source of the object.
-  SourceType source_type = 11;
+  SourceType source_type = 13;
   // checksums define the root hash of the pieces which stored in a SP.
   // add omit tag to omit the field when converting to NFT metadata
-  repeated bytes checksums = 12 [(gogoproto.moretags) = "traits:\"omit\""];
-  // secondary_sp_addresses define the addresses of secondary_sps
-  repeated string secondary_sp_addresses = 13 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  repeated bytes checksums = 14 [(gogoproto.moretags) = "traits:\"omit\""];
 }
 
 message GroupInfo {
@@ -166,3 +164,15 @@ message DeleteInfo {
   Ids object_ids = 2;
   Ids group_ids = 3;
 }
+
+message MigrationBucketInfo {
+  uint32 src_sp_id = 1;
+  uint32 src_global_virtual_group_family_id = 2;
+  uint32 dst_sp_id = 3;
+  // id is the unique identifier of bucket
+  string bucket_id = 4 [
+    (cosmos_proto.scalar) = "cosmos.Uint",
+    (gogoproto.customtype) = "Uint",
+    (gogoproto.nullable) = false
+  ];
+}
diff --git a/proto/greenfield/virtualgroup/events.proto b/proto/greenfield/virtualgroup/events.proto
new file mode 100644
index 000000000..6fe9aed0f
--- /dev/null
+++ b/proto/greenfield/virtualgroup/events.proto
@@ -0,0 +1,139 @@
+syntax = "proto3";
+package greenfield.virtualgroup;
+
+import "cosmos_proto/cosmos.proto";
+import "gogoproto/gogo.proto";
+
+option go_package = "github.com/bnb-chain/greenfield/x/virtualgroup/types";
+
+message EventCreateGlobalVirtualGroup {
+  // The unique id of global virtual group, which is generated by blockchain
+  uint32 id = 1;
+
+  // The id of the global virtual group family where the gvg belongs
+  uint32 family_id = 2;
+
+  // The id of the primary sp who create this global virtual group
+  uint32 primary_sp_id = 3;
+
+  // The ids of the secondary sps which belongs to this global virtual group
+  repeated uint32 secondary_sp_ids = 4;
+
+  // The store size of all the objects stores in this global virtual group
+  uint64 stored_size = 5;
+
+  // The virtual payment address of this global virtual group, which is auto generated by blockchain
+  // And, all users' payment flows will flow to this account
+  string virtual_payment_address = 6 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // The total amount of the staking for this global virtual group
+  string total_deposit = 7 [
+    (cosmos_proto.scalar) = "cosmos.Int",
+    (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
+    (gogoproto.nullable) = false
+  ];
+}
+
+message EventCreateGlobalVirtualGroupFamily {
+  // The id of global virtual group family, which is auto generated by blockchain
+  uint32 id = 1;
+
+  // The virtual payment address of this global virtual group family, which is auto generated by blockcahin
+  // all users' read quota payment flows will flow to this account.
+  string virtual_payment_address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+}
+
+message EventDeleteGlobalVirtualGroup {
+  // The id of global virtual group, which has been deleted
+  uint32 id = 1;
+}
+
+message EventUpdateGlobalVirtualGroup {
+  // The id of global virtual group, which has been updated
+  uint32 id = 1;
+  // The store size of all the objects stores in this global virtual group
+  uint64 store_size = 2;
+  // The total amount of the staking for this global virtual group
+  string total_deposit = 3 [
+    (cosmos_proto.scalar) = "cosmos.Int",
+    (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
+    (gogoproto.nullable) = false
+  ];
+}
+
+message EventCreateLocalVirtualGroup {
+  // The id of the local virtual group and this ID is unique within the bucket
+  // and different buckets will have the same ID
+  uint32 id = 1;
+  // The id of the bucket
+  string bucket_id = 2 [
+    (cosmos_proto.scalar) = "cosmos.Uint",
+    (gogoproto.customtype) = "Uint",
+    (gogoproto.nullable) = false
+  ];
+  // The id of the global virtual group
+  uint32 global_virtual_group_id = 3;
+  // The stored size of all the objects stores in this lvg
+  uint64 stored_size = 4;
+}
+
+message EventUpdateLocalVirtualGroup {
+  // The id of the local virtual group
+  uint32 id = 1;
+  // The id of the global virtual group
+  uint32 global_virtual_group_id = 2;
+  // The stored size of all the objects stores in this lvg
+  uint64 stored_size = 3;
+}
+
+message EventSwapOut {
+  // The id of the storage provider who wants to swap out
+  uint32 storage_provider_id = 1;
+  // The id of the gvg family which the storage provider wants to swap out as primary sp
+  uint32 global_virtual_group_family_id = 2;
+  // The ids of the gvgs which the storage provider wants to swap out as secondary sp
+  repeated uint32 global_virtual_group_ids = 3;
+  // The id of the successor sp who take over this family or these gvgs
+  uint32 successor_sp_id = 4;
+}
+
+message EventCompleteSwapOut {
+  // The id of the storage provider who complete swap out.
+  uint32 storage_provider_id = 1;
+  // The id of the storage provider who swap out the family or gvgs
+  uint32 src_storage_provider_id = 2;
+  // The id of the gvg family
+  uint32 global_virtual_group_family_id = 3;
+  // The ids of the gvgs
+  repeated uint32 global_virtual_group_ids = 4;
+}
+
+message EventCancelSwapOut {
+  // The id of the storage provider who cancel swap out.
+  uint32 storage_provider_id = 1;
+  // The id of the gvg family
+  uint32 global_virtual_group_family_id = 2;
+  // The ids of the gvgs
+  repeated uint32 global_virtual_group_ids = 3;
+  // The id of the successor sp who take over this family or these gvgs
+  uint32 successor_sp_id = 4;
+}
+
+message EventStorageProviderExit {
+  // The id of the storage provider who wants to exit
+  uint32 storage_provider_id = 1;
+  // The operator address of the storage provider who wants to exit
+  string operator_address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+}
+
+message EventCompleteStorageProviderExit {
+  // The id of the storage provider who complete exit
+  uint32 storage_provider_id = 1;
+  // The operator address which initial the complete exit transaction
+  string operator_address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // total_deposit defines the number of tokens deposited by this storage provider for staking.
+  string total_deposit = 3 [
+    (cosmos_proto.scalar) = "cosmos.Int",
+    (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
+    (gogoproto.nullable) = false
+  ];
+}
diff --git a/proto/greenfield/virtualgroup/genesis.proto b/proto/greenfield/virtualgroup/genesis.proto
new file mode 100644
index 000000000..5e2600517
--- /dev/null
+++ b/proto/greenfield/virtualgroup/genesis.proto
@@ -0,0 +1,16 @@
+syntax = "proto3";
+package greenfield.virtualgroup;
+
+import "gogoproto/gogo.proto";
+import "greenfield/virtualgroup/params.proto";
+
+option go_package = "github.com/bnb-chain/greenfield/x/virtualgroup/types";
+
+// this line is used by starport scaffolding # genesis/proto/import
+
+// GenesisState defines the virtualgroup module's genesis state.
+// GenesisState defines the raw genesis transaction in JSON.
+message GenesisState {
+  Params params = 1 [(gogoproto.nullable) = false];
+  // this line is used by starport scaffolding # genesis/proto/state
+}
diff --git a/proto/greenfield/virtualgroup/params.proto b/proto/greenfield/virtualgroup/params.proto
new file mode 100644
index 000000000..ea37f8004
--- /dev/null
+++ b/proto/greenfield/virtualgroup/params.proto
@@ -0,0 +1,28 @@
+syntax = "proto3";
+package greenfield.virtualgroup;
+
+import "cosmos_proto/cosmos.proto";
+import "gogoproto/gogo.proto";
+
+option go_package = "github.com/bnb-chain/greenfield/x/virtualgroup/types";
+
+// Params defines the parameters for the module.
+message Params {
+  option (gogoproto.equal) = true;
+  option (gogoproto.goproto_stringer) = false;
+
+  // deposit_denom defines the staking coin denomination.
+  string deposit_denom = 1;
+  // store price, in bnb wei per charge byte
+  string gvg_staking_per_bytes = 2 [
+    (cosmos_proto.scalar) = "cosmos.Int",
+    (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
+    (gogoproto.nullable) = false
+  ];
+  // the max number of lvg which allowed in a bucket
+  uint32 max_local_virtual_group_num_per_bucket = 3;
+  // the max number of gvg which can exist in a family
+  uint32 max_global_virtual_group_num_per_family = 4;
+  // if the store size reach the exceed, the family is not allowed to sever more buckets
+  uint64 max_store_size_per_family = 5;
+}
diff --git a/proto/greenfield/virtualgroup/query.proto b/proto/greenfield/virtualgroup/query.proto
new file mode 100644
index 000000000..9f3f14b97
--- /dev/null
+++ b/proto/greenfield/virtualgroup/query.proto
@@ -0,0 +1,89 @@
+syntax = "proto3";
+
+package greenfield.virtualgroup;
+
+import "cosmos/base/query/v1beta1/pagination.proto";
+import "gogoproto/gogo.proto";
+import "google/api/annotations.proto";
+import "greenfield/virtualgroup/params.proto";
+import "greenfield/virtualgroup/types.proto";
+
+// this line is used by starport scaffolding # 1
+
+option go_package = "github.com/bnb-chain/greenfield/x/virtualgroup/types";
+
+// Query defines the gRPC query service.
+service Query {
+  // Parameters queries the parameters of the module.
+  rpc Params(QueryParamsRequest) returns (QueryParamsResponse) {
+    option (google.api.http).get = "/greenfield/virtualgroup/params";
+  }
+
+  // Queries a global virtual group by its id.
+  rpc GlobalVirtualGroup(QueryGlobalVirtualGroupRequest) returns (QueryGlobalVirtualGroupResponse) {
+    option (google.api.http).get = "/greenfield/virtualgroup/global_virtual_group";
+  }
+
+  // Queries a list of global virtual groups by family id.
+  rpc GlobalVirtualGroupByFamilyID(QueryGlobalVirtualGroupByFamilyIDRequest) returns (QueryGlobalVirtualGroupByFamilyIDResponse) {
+    option (google.api.http).get = "/greenfield/virtualgroup/global_virtual_group_by_family_id";
+  }
+
+  // Queries a list of global virtual group families by storage provider id.
+  rpc GlobalVirtualGroupFamilies(QueryGlobalVirtualGroupFamiliesRequest) returns (QueryGlobalVirtualGroupFamiliesResponse) {
+    option (google.api.http).get = "/greenfield/virtualgroup/global_virtual_group_families";
+  }
+
+  // Queries a global virtual group family by its id.
+  rpc GlobalVirtualGroupFamily(QueryGlobalVirtualGroupFamilyRequest) returns (QueryGlobalVirtualGroupFamilyResponse) {
+    option (google.api.http).get = "/greenfield/virtualgroup/global_virtual_group_family";
+  }
+
+  // this line is used by starport scaffolding # 2
+}
+
+// QueryParamsRequest is request type for the Query/Params RPC method.
+message QueryParamsRequest {}
+
+// QueryParamsResponse is response type for the Query/Params RPC method.
+message QueryParamsResponse {
+  // params holds all the parameters of this module.
+  Params params = 1 [(gogoproto.nullable) = false];
+}
+
+message QueryGlobalVirtualGroupRequest {
+  uint32 global_virtual_group_id = 1;
+}
+
+message QueryGlobalVirtualGroupResponse {
+  GlobalVirtualGroup global_virtual_group = 1;
+}
+
+message QueryGlobalVirtualGroupByFamilyIDRequest {
+  uint32 storage_provider_id = 1;
+  uint32 global_virtual_group_family_id = 2;
+}
+
+message QueryGlobalVirtualGroupByFamilyIDResponse {
+  repeated GlobalVirtualGroup global_virtual_groups = 1;
+}
+
+message QueryGlobalVirtualGroupFamiliesRequest {
+  uint32 storage_provider_id = 1;
+  cosmos.base.query.v1beta1.PageRequest pagination = 2;
+}
+
+message QueryGlobalVirtualGroupFamiliesResponse {
+  repeated GlobalVirtualGroupFamily global_virtual_group_families = 1;
+  cosmos.base.query.v1beta1.PageResponse pagination = 2;
+}
+
+message QueryGlobalVirtualGroupFamilyRequest {
+  uint32 storage_provider_id = 1;
+  uint32 family_id = 2;
+}
+
+message QueryGlobalVirtualGroupFamilyResponse {
+  GlobalVirtualGroupFamily global_virtual_group_family = 1;
+}
+// this line is used by starport scaffolding # 3
diff --git a/proto/greenfield/virtualgroup/tx.proto b/proto/greenfield/virtualgroup/tx.proto
new file mode 100644
index 000000000..a0026bf1d
--- /dev/null
+++ b/proto/greenfield/virtualgroup/tx.proto
@@ -0,0 +1,191 @@
+syntax = "proto3";
+
+package greenfield.virtualgroup;
+
+import "cosmos/base/v1beta1/coin.proto";
+import "cosmos/msg/v1/msg.proto";
+// this line is used by starport scaffolding # proto/tx/import
+import "cosmos_proto/cosmos.proto";
+import "gogoproto/gogo.proto";
+import "greenfield/common/approval.proto";
+import "greenfield/virtualgroup/params.proto";
+
+option go_package = "github.com/bnb-chain/greenfield/x/virtualgroup/types";
+
+// Msg defines the Msg service.
+service Msg {
+  // this line is used by starport scaffolding # proto/tx/import
+  rpc CreateGlobalVirtualGroup(MsgCreateGlobalVirtualGroup) returns (MsgCreateGlobalVirtualGroupResponse);
+  rpc DeleteGlobalVirtualGroup(MsgDeleteGlobalVirtualGroup) returns (MsgDeleteGlobalVirtualGroupResponse);
+  rpc Deposit(MsgDeposit) returns (MsgDepositResponse);
+  rpc Withdraw(MsgWithdraw) returns (MsgWithdrawResponse);
+  rpc SwapOut(MsgSwapOut) returns (MsgSwapOutResponse);
+  rpc Settle(MsgSettle) returns (MsgSettleResponse);
+
+  // UpdateParams defines a governance operation for updating the x/virtual module parameters.
+  // The authority is defined in the keeper.
+
+  // Since: cosmos-sdk 0.47
+  rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse);
+  rpc StorageProviderExit(MsgStorageProviderExit) returns (MsgStorageProviderExitResponse);
+  rpc CompleteStorageProviderExit(MsgCompleteStorageProviderExit) returns (MsgCompleteStorageProviderExitResponse);
+  rpc CompleteSwapOut(MsgCompleteSwapOut) returns (MsgCompleteSwapOutResponse);
+  rpc CancelSwapOut(MsgCancelSwapOut) returns (MsgCancelSwapOutResponse);
+}
+
+// MsgUpdateParams is the Msg/UpdateParams request type.
+message MsgUpdateParams {
+  option (cosmos.msg.v1.signer) = "authority";
+
+  // authority is the address that controls the module (defaults to x/gov unless overwritten).
+  string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // params defines the x/virtualgroup parameters to update.
+  // NOTE: All parameters must be supplied.
+  Params params = 2 [(gogoproto.nullable) = false];
+}
+
+// MsgUpdateParamsResponse defines the response structure for executing a
+// MsgUpdateParams message.
+message MsgUpdateParamsResponse {}
+
+message MsgCreateGlobalVirtualGroup {
+  option (cosmos.msg.v1.signer) = "storage_provider";
+
+  // storage_provider defines the operator account address of the storage provider who create the global virtual group.
+  string storage_provider = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // family_id is the identifier for the virtual group's family.
+  uint32 family_id = 2;
+  // secondary_sp_id is a list of secondary storage provider IDs associated with the virtual group.
+  repeated uint32 secondary_sp_ids = 3;
+  // total_deposit is the total deposit amount required for the virtual group.
+  // The tokens needs deposited and the size of storage are correlated.
+  cosmos.base.v1beta1.Coin deposit = 4 [(gogoproto.nullable) = false];
+}
+
+message MsgCreateGlobalVirtualGroupResponse {}
+
+message MsgDeleteGlobalVirtualGroup {
+  option (cosmos.msg.v1.signer) = "storage_provider";
+
+  // storage_provider defines the operator account address of the storage provider who delete the global virtual group.
+  string storage_provider = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // global_virtual_group_id is the identifier of the global virtual group.
+  uint32 global_virtual_group_id = 2;
+}
+
+message MsgDeleteGlobalVirtualGroupResponse {}
+
+message MsgDeposit {
+  option (cosmos.msg.v1.signer) = "storage_provider";
+
+  // storage_provider defines the operator/funding account address of the storage provider who deposit to the global virtual group.
+  string storage_provider = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // global_virtual_group_id is the identifier of the global virtual group.
+  uint32 global_virtual_group_id = 2;
+  // deposit is the amount of tokens being deposited for the global virtual group.
+  cosmos.base.v1beta1.Coin deposit = 3 [(gogoproto.nullable) = false];
+}
+
+message MsgDepositResponse {}
+
+message MsgWithdraw {
+  option (cosmos.msg.v1.signer) = "storage_provider";
+
+  // storage_provider defines the operator/funding account address of the storage provider who withdraw from the global virtual group.
+  string storage_provider = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // global_virtual_group_id is the identifier of the global virtual group.
+  uint32 global_virtual_group_id = 2;
+  // withdraw is the amount of coins to be withdrawn.
+  // The amount needs to be smaller than stored_size * storage_staking_price
+  cosmos.base.v1beta1.Coin withdraw = 3 [(gogoproto.nullable) = false];
+}
+
+message MsgWithdrawResponse {}
+
+message MsgSwapOut {
+  option (cosmos.msg.v1.signer) = "storage_provider";
+
+  // storage_provider defines the operator account address of the storage provider who want to swap out from the global virtual group.
+  string storage_provider = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // virtual_group_family_id is the identifier of the virtual group family.
+  // if it set to non-zero, it represents that the operator swap out as the primary storage provider
+  // it it set to zero, it represents that the operator swap out as the secondary storage provider.
+  uint32 global_virtual_group_family_id = 2;
+  // global_virtual_group_ids is a list of global virtual group IDs associated with the swap out.
+  // It allows to be empty only when the operator is the primary storage provider.
+  repeated uint32 global_virtual_group_ids = 3;
+  // successor_sp_id is the unique id of the successor storage provider.
+  uint32 successor_sp_id = 4;
+  // approval includes an expiration time and a signature.
+  // The fields to be signed with contains the necessary information of the successor.
+  common.Approval successor_sp_approval = 5;
+}
+
+message MsgSwapOutResponse {}
+
+message MsgCompleteSwapOut {
+  option (cosmos.msg.v1.signer) = "storage_provider";
+
+  // storage_provider defines the operator account address of the storage provider who complete swap out task.
+  string storage_provider = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // virtual_group_family_id is the identifier of the virtual group family.
+  // if it set to non-zero, it represents that the operator swap out as the primary storage provider
+  // it it set to zero, it represents that the operator swap out as the secondary storage provider.
+  uint32 global_virtual_group_family_id = 2;
+  // global_virtual_group_ids is a list of global virtual group IDs associated with the swap out.
+  // It allows to be empty only when the operator is the primary storage provider.
+  repeated uint32 global_virtual_group_ids = 3;
+}
+
+message MsgCompleteSwapOutResponse {}
+
+message MsgCancelSwapOut {
+  option (cosmos.msg.v1.signer) = "storage_provider";
+
+  // storage_provider defines the operator account address of the storage provider who cancel the swap out task.
+  string storage_provider = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // virtual_group_family_id is the identifier of the virtual group family.
+  // if it set to non-zero, it represents that the operator swap out as the primary storage provider
+  // it it set to zero, it represents that the operator swap out as the secondary storage provider.
+  uint32 global_virtual_group_family_id = 2;
+  // global_virtual_group_ids is a list of global virtual group IDs associated with the swap out.
+  // It allows to be empty only when the operator is the primary storage provider.
+  repeated uint32 global_virtual_group_ids = 3;
+}
+
+message MsgCancelSwapOutResponse {}
+
+// MsgSettle define the message for settling storage income of GVG family or several GVGs.
+// Firstly, the handler will do stream settlement for the payment account; and
+// secondly, the income will be distributed to related storage providers.
+message MsgSettle {
+  option (cosmos.msg.v1.signer) = "storage_provider";
+
+  // storage_provider defines the operator/funding account address of the storage provider who initial settle request.
+  string storage_provider = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // global_virtual_group_family_id is the identifier of the global virtual group family.
+  uint32 global_virtual_group_family_id = 2;
+  // global_virtual_group_id is the identifier of the global virtual group.
+  repeated uint32 global_virtual_group_ids = 3;
+}
+
+message MsgSettleResponse {}
+
+// this line is used by starport scaffolding # proto/tx/message
+message MsgStorageProviderExit {
+  option (cosmos.msg.v1.signer) = "storage_provider";
+
+  // storage_provider defines the operator account address of the storage provider who want to exit from the greenfield storage network.
+  string storage_provider = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+}
+
+message MsgStorageProviderExitResponse {}
+
+message MsgCompleteStorageProviderExit {
+  option (cosmos.msg.v1.signer) = "storage_provider";
+
+  // storage_provider defines the operator account address of the storage provider who want to exit from the greenfield storage network.
+  string storage_provider = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+}
+
+message MsgCompleteStorageProviderExitResponse {}
diff --git a/proto/greenfield/virtualgroup/types.proto b/proto/greenfield/virtualgroup/types.proto
new file mode 100644
index 000000000..393097b08
--- /dev/null
+++ b/proto/greenfield/virtualgroup/types.proto
@@ -0,0 +1,70 @@
+syntax = "proto3";
+package greenfield.virtualgroup;
+
+import "cosmos_proto/cosmos.proto";
+import "gogoproto/gogo.proto";
+
+option go_package = "github.com/bnb-chain/greenfield/x/virtualgroup/types";
+
+// A global virtual group consists of one primary SP (SP) and multiple secondary SP.
+// Every global virtual group must belong to a GVG family, and the objects of each
+// bucket must be stored in a GVG within a group family.
+message GlobalVirtualGroup {
+  // ID represents the unique identifier of the global virtual group.
+  uint32 id = 1;
+  // Family ID represents the identifier of the GVG family that the group belongs to.
+  uint32 family_id = 2;
+  // Primary SP ID represents the unique identifier of the primary storage provider in the group.
+  uint32 primary_sp_id = 3;
+  // Secondary SP IDs represents the list of unique identifiers of the secondary storage providers in the group.
+  repeated uint32 secondary_sp_ids = 4;
+  // Stored size represents the size of the stored objects within the group.
+  uint64 stored_size = 5;
+  // Virtual payment address represents the payment address associated with the group.
+  string virtual_payment_address = 6 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+  // Total deposit represents the number of tokens deposited by this storage provider for staking.
+  string total_deposit = 7 [
+    (cosmos_proto.scalar) = "cosmos.Int",
+    (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
+    (gogoproto.nullable) = false
+  ];
+}
+
+// Global virtual group family serve as a means of grouping global virtual groups.
+// Each bucket must be associated with a unique global virtual group family and cannot cross families.
+message GlobalVirtualGroupFamily {
+  // id is the identifier of the global virtual group family.
+  uint32 id = 1;
+  // global_virtual_group_ids is a list of identifiers of the global virtual groups associated with the family.
+  repeated uint32 global_virtual_group_ids = 2;
+  // virtual_payment_address is the payment address associated with the global virtual group family.
+  string virtual_payment_address = 3 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+}
+
+message GlobalVirtualGroupsBindingOnBucket {
+  // bucket_id is the unique identification for the bucket.
+  string bucket_id = 1 [
+    (cosmos_proto.scalar) = "cosmos.Uint",
+    (gogoproto.customtype) = "Uint",
+    (gogoproto.nullable) = false
+  ];
+  // global_virtual_group_ids is a list of identifiers of the global virtual groups associated with the bucket.
+  repeated uint32 global_virtual_group_ids = 2;
+  // local_virtual_group_ids is a list of identifiers of the local virtual groups associated with the bucket.
+  repeated uint32 local_virtual_group_ids = 3;
+}
+
+message GVGStatisticsWithinSP {
+  // storage_provider_id defines the id of the sp which the statistics associated to
+  uint32 storage_provider_id = 1;
+  // secondary_count defines the number of global virtual groups (GVGs) in
+  // which this storage provider serves as a secondary storage provider.
+  uint32 secondary_count = 3;
+}
+
+message SwapOutInfo {
+  // sp_id is the unique id of the storage provider who want to swap out.
+  uint32 sp_id = 1;
+  // successor_sp_id is the id of the successor storage provider.
+  uint32 successor_sp_id = 2;
+}
diff --git a/readme.md b/readme.md
index 43c53120f..b1fbd59b7 100644
--- a/readme.md
+++ b/readme.md
@@ -6,7 +6,7 @@ Official Golang implementation of the Greenfield Blockchain. It uses [cometbft](
 for consensus and build on [cosmos-sdk](https://github.com/cosmos/cosmos-sdk).
 
 BNB Greenfield aims to facilitate the decentralized data economy by simplifying the process of storing and managing data
-access, as well as linking data ownership with the massive DeFi context of the Binance Smart Chain (BSC).
+access, as well as linking data ownership with the massive DeFi context of the BNB Smart Chain (BSC).
 
 BNB Greenfield operates through three core components, which distinguish it from existing centralized and decentralized
 storage systems::
@@ -34,7 +34,7 @@ The center of BNB Greenfield are two layers:
 This repo is the official implementation of Greenfield blockchain. 
 
 The blockchain of BNB Greenfield serves a dual purpose of maintaining the ledger for users as well as the storage metadata
-as common blockchain state data. The blockchain has its native token, BNB, which is transferred from the Binance Smart Chain,
+as common blockchain state data. The blockchain has its native token, BNB, which is transferred from the BNB Smart Chain,
 and is utilized for gas and governance functionalities. Governance is further enabled through the staking logic that is
 unique to the BNB Greenfield blockchain.
 
diff --git a/scripts/mockgen.sh b/scripts/mockgen.sh
new file mode 100644
index 000000000..ecd14615b
--- /dev/null
+++ b/scripts/mockgen.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+
+mockgencmd="mockgen"
+
+${mockgencmd} -source=x/challenge/types/expected_keepers.go -destination=x/challenge/types/expected_keepers_mocks.go -package=types
+${mockgencmd} -source=x/payment/types/expected_keepers.go -destination=x/payment/types/expected_keepers_mocks.go -package=types
+${mockgencmd} -source=x/permission/types/expected_keepers.go -destination=x/permission/types/expected_keepers_mocks.go -package=types
+${mockgencmd} -source=x/sp/types/expected_keepers.go -destination=x/sp/types/expected_keepers_mocks.go -package=types
+${mockgencmd} -source=x/storage/types/expected_keepers.go -destination=x/storage/types/expected_keepers_mocks.go -package=types
+${mockgencmd} -source=x/virtualgroup/types/expected_keepers.go -destination=x/virtualgroup/types/expected_keepers_mocks.go -package=types
diff --git a/sdk/client/gnfd_client.go b/sdk/client/gnfd_client.go
index 31aa07aa3..ea3ce9aa0 100644
--- a/sdk/client/gnfd_client.go
+++ b/sdk/client/gnfd_client.go
@@ -34,6 +34,8 @@ import (
 	paymenttypes "github.com/bnb-chain/greenfield/x/payment/types"
 	sptypes "github.com/bnb-chain/greenfield/x/sp/types"
 	storagetypes "github.com/bnb-chain/greenfield/x/storage/types"
+	virtualgroupmoduletypes "github.com/bnb-chain/greenfield/x/virtualgroup/types"
+	bfthttp "github.com/cometbft/cometbft/rpc/client/http"
 )
 
 // AuthQueryClient is a type to define the auth types Query Client
@@ -90,6 +92,9 @@ type TxClient = tx.ServiceClient
 // UpgradeQueryClient is a type to define the upgrade types Query Client
 type UpgradeQueryClient = upgradetypes.QueryClient
 
+// VirtualGroupQueryClient is a type to define the virtual group types Query Client
+type VirtualGroupQueryClient = virtualgroupmoduletypes.QueryClient
+
 // TmClient is a type to define the tendermint service client
 type TmClient = tmservice.ServiceClient
 
@@ -129,11 +134,14 @@ type GreenfieldClient struct {
 	StakingQueryClient
 	// UpgradeQueryClient holds the upgrade query client.
 	UpgradeQueryClient
+	// VirtualGroupQueryClient holds the virtual group query client
+	VirtualGroupQueryClient
 	// TxClient holds the tx service client.
 	TxClient
 	// TmService holds the tendermint service client
 	TmClient
-
+	// tendermintClient directly interact with tendermint Node
+	tendermintClient *bfthttp.HTTP
 	// keyManager is the manager used for generating and managing keys.
 	keyManager keys.KeyManager
 	// chainId is the id of the chain.
@@ -170,6 +178,7 @@ func newGreenfieldClient(rpcAddr, chainId string, rpcClient *rpchttp.HTTP, opts
 		chainId: chainId,
 		codec:   cdc,
 	}
+	client.tendermintClient = rpcClient
 	for _, opt := range opts {
 		opt.Apply(client)
 	}
@@ -211,6 +220,7 @@ func setClientsConn(c *GreenfieldClient, conn grpc1.ClientConn) {
 	c.SlashingQueryClient = slashingtypes.NewQueryClient(conn)
 	c.StakingQueryClient = stakingtypes.NewQueryClient(conn)
 	c.UpgradeQueryClient = upgradetypes.NewQueryClient(conn)
+	c.VirtualGroupQueryClient = virtualgroupmoduletypes.NewQueryClient(conn)
 	c.TmClient = tmservice.NewServiceClient(conn)
 	c.TxClient = tx.NewServiceClient(conn)
 }
diff --git a/sdk/client/gnfd_tm.go b/sdk/client/gnfd_tm.go
new file mode 100644
index 000000000..39e3f97fa
--- /dev/null
+++ b/sdk/client/gnfd_tm.go
@@ -0,0 +1,42 @@
+package client
+
+import (
+	"context"
+	"encoding/hex"
+
+	ctypes "github.com/cometbft/cometbft/rpc/core/types"
+)
+
+// GetBlock by height, gets the latest block if height is nil
+func (c *GreenfieldClient) GetBlock(ctx context.Context, height *int64) (*ctypes.ResultBlock, error) {
+	return c.tendermintClient.Block(ctx, height)
+}
+
+// Tx gets a tx by detail by the tx hash
+func (c *GreenfieldClient) Tx(ctx context.Context, txHash string) (*ctypes.ResultTx, error) {
+	hash, err := hex.DecodeString(txHash)
+	if err != nil {
+		return nil, err
+	}
+	return c.tendermintClient.Tx(ctx, hash, true)
+}
+
+// GetBlockResults by height, gets the latest block result if height is nil
+func (c *GreenfieldClient) GetBlockResults(ctx context.Context, height *int64) (*ctypes.ResultBlockResults, error) {
+	return c.tendermintClient.BlockResults(ctx, height)
+}
+
+// GetValidators by height, gets the latest validators if height is nil
+func (c *GreenfieldClient) GetValidators(ctx context.Context, height *int64) (*ctypes.ResultValidators, error) {
+	return c.tendermintClient.Validators(ctx, height, nil, nil)
+}
+
+// GetHeader by height, gets the latest block header if height is nil
+func (c *GreenfieldClient) GetHeader(ctx context.Context, height *int64) (*ctypes.ResultHeader, error) {
+	return c.tendermintClient.Header(ctx, height)
+}
+
+// GetUnconfirmedTxs by height, gets the latest block header if height is nil
+func (c *GreenfieldClient) GetUnconfirmedTxs(ctx context.Context, limit *int) (*ctypes.ResultUnconfirmedTxs, error) {
+	return c.tendermintClient.UnconfirmedTxs(ctx, limit)
+}
diff --git a/sdk/client/gnfd_tm_test.go b/sdk/client/gnfd_tm_test.go
new file mode 100644
index 000000000..73cddc589
--- /dev/null
+++ b/sdk/client/gnfd_tm_test.go
@@ -0,0 +1,53 @@
+package client
+
+import (
+	"context"
+	"testing"
+	"time"
+
+	"github.com/bnb-chain/greenfield/sdk/client/test"
+	"github.com/bnb-chain/greenfield/sdk/keys"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestTmClient(t *testing.T) {
+	km, err := keys.NewPrivateKeyManager(test.TEST_PRIVATE_KEY)
+	assert.NoError(t, err)
+	gnfdCli, err := NewGreenfieldClient(test.TEST_RPC_ADDR, test.TEST_CHAIN_ID, WithKeyManager(km))
+	assert.NoError(t, err)
+	to, err := sdk.AccAddressFromHexUnsafe(test.TEST_ADDR)
+	assert.NoError(t, err)
+	transfer := banktypes.NewMsgSend(km.GetAddr(), to, sdk.NewCoins(sdk.NewInt64Coin(test.TEST_TOKEN_NAME, 12)))
+	response, err := gnfdCli.BroadcastTx(context.Background(), []sdk.Msg{transfer}, nil)
+	assert.NoError(t, err)
+	time.Sleep(2 * time.Second)
+	// get tx
+	res, err := gnfdCli.Tx(context.Background(), response.TxResponse.TxHash)
+	assert.NoError(t, err)
+	t.Log(res.Hash)
+	t.Log(res.TxResult.String())
+
+	// get the latest block
+	block, err := gnfdCli.GetBlock(context.Background(), nil)
+	assert.NoError(t, err)
+	t.Log(block)
+
+	h := block.Block.Height
+
+	block, err = gnfdCli.GetBlock(context.Background(), &h)
+	assert.NoError(t, err)
+	t.Log(block)
+
+	// get block result
+	blockResult, err := gnfdCli.GetBlockResults(context.Background(), &h)
+	assert.NoError(t, err)
+	t.Log(blockResult)
+
+	// get validator
+	validators, err := gnfdCli.GetValidators(context.Background(), &h)
+	assert.NoError(t, err)
+	t.Log(validators.Validators)
+
+}
diff --git a/sdk/keys/key_manager.go b/sdk/keys/key_manager.go
index 61f8423a5..775ca3d9c 100644
--- a/sdk/keys/key_manager.go
+++ b/sdk/keys/key_manager.go
@@ -6,6 +6,7 @@ import (
 	"strings"
 
 	"github.com/cosmos/cosmos-sdk/crypto/hd"
+	ethbls "github.com/cosmos/cosmos-sdk/crypto/keys/eth/bls"
 	"github.com/cosmos/cosmos-sdk/crypto/keys/eth/ethsecp256k1"
 	ctypes "github.com/cosmos/cosmos-sdk/crypto/types"
 	"github.com/cosmos/cosmos-sdk/types"
@@ -41,6 +42,12 @@ func NewMnemonicKeyManager(mnemonic string) (KeyManager, error) {
 	return &k, err
 }
 
+func NewBlsPrivateKeyManager(priKey string) (KeyManager, error) {
+	k := keyManager{}
+	err := k.recoveryFromBlsPrivateKey(priKey)
+	return &k, err
+}
+
 func NewBlsMnemonicKeyManager(mnemonic string) (KeyManager, error) {
 	k := keyManager{}
 	err := k.recoveryBlsFromMnemonic(mnemonic, FullPath)
@@ -86,6 +93,23 @@ func (km *keyManager) recoveryFromMnemonic(mnemonic, keyPath string) error {
 	return nil
 }
 
+func (km *keyManager) recoveryFromBlsPrivateKey(privateKey string) error {
+	priBytes, err := hex.DecodeString(privateKey)
+	if err != nil {
+		return err
+	}
+
+	if len(priBytes) != 32 {
+		return fmt.Errorf("Len of Keybytes is not equal to 32 ")
+	}
+	var keyBytesArray [32]byte
+	copy(keyBytesArray[:], priBytes[:32])
+	priKey := hd.EthBLS.Generate()(keyBytesArray[:]).(*ethbls.PrivKey)
+	km.privKey = priKey
+	km.addr = types.AccAddress(km.privKey.PubKey().Address())
+	return nil
+}
+
 func (km *keyManager) recoveryBlsFromMnemonic(mnemonic, keyPath string) error {
 	words := strings.Split(mnemonic, " ")
 	if len(words) != 12 && len(words) != 24 {
diff --git a/sdk/keys/key_manager_test.go b/sdk/keys/key_manager_test.go
index 498ecf85e..e1836ed31 100644
--- a/sdk/keys/key_manager_test.go
+++ b/sdk/keys/key_manager_test.go
@@ -1,8 +1,10 @@
 package keys
 
 import (
+	"encoding/hex"
 	"testing"
 
+	"github.com/prysmaticlabs/prysm/crypto/bls"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -24,3 +26,11 @@ func TestCreateKeyManagerFromMnemonic(t *testing.T) {
 	address := keyManager.GetAddr().String()
 	assert.Equal(t, "0x535E34B319B3575108Deaf2f4FEeeC73AEbE3eF9", address)
 }
+
+func TestCreateBlsKeyManagerFromPrivateKeyHex(t *testing.T) {
+	blsPrivKey, _ := bls.RandKey()
+	blsPubKey := hex.EncodeToString(blsPrivKey.PublicKey().Marshal())
+	km, err := NewBlsPrivateKeyManager(hex.EncodeToString(blsPrivKey.Marshal()))
+	assert.NoError(t, err)
+	assert.Equal(t, blsPubKey, hex.EncodeToString(km.PubKey().Bytes()))
+}
diff --git a/swagger/static/swagger.yaml b/swagger/static/swagger.yaml
index 6c5e2942d..c106e626b 100644
--- a/swagger/static/swagger.yaml
+++ b/swagger/static/swagger.yaml
@@ -53,6 +53,214 @@ paths:
                       format: byte
       tags:
         - Query
+  /greenfield/challenge/inturn_attestation_submitter:
+    get:
+      summary: Queries the inturn challenger.
+      operationId: InturnAttestationSubmitter
+      responses:
+        '200':
+          description: A successful response.
+          schema:
+            type: object
+            properties:
+              bls_pub_key:
+                type: string
+              submit_interval:
+                type: object
+                properties:
+                  start:
+                    type: string
+                    format: uint64
+                  end:
+                    type: string
+                    format: uint64
+                description: >-
+                  SubmitInterval holds start and end (exclusive) (i.e., [start,
+                  end)) time of in turn attestation.
+            description: >-
+              QueryInturnAttestationSubmitterResponse is response type for the
+              Query/InturnAttestationSubmitter RPC method.
+        default:
+          description: An unexpected error response.
+          schema:
+            type: object
+            properties:
+              error:
+                type: string
+              code:
+                type: integer
+                format: int32
+              message:
+                type: string
+              details:
+                type: array
+                items:
+                  type: object
+                  properties:
+                    type_url:
+                      type: string
+                    value:
+                      type: string
+                      format: byte
+      tags:
+        - Query
+  /greenfield/challenge/latest_attested_challenges:
+    get:
+      summary: Queries the latest attested challenges.
+      operationId: LatestAttestedChallenges
+      responses:
+        '200':
+          description: A successful response.
+          schema:
+            type: object
+            properties:
+              challenges:
+                type: array
+                items:
+                  type: object
+                  properties:
+                    id:
+                      type: string
+                      format: uint64
+                      description: The id of the challenge.
+                    result:
+                      description: The attestation result of the challenge.
+                      type: string
+                      enum:
+                        - CHALLENGE_FAILED
+                        - CHALLENGE_SUCCEED
+                      default: CHALLENGE_FAILED
+                  description: AttestedChallenge records the challenge which are attested.
+            description: >-
+              QueryLatestAttestedChallengesResponse is response type for the
+              Query/LatestAttestedChallenges RPC method.
+        default:
+          description: An unexpected error response.
+          schema:
+            type: object
+            properties:
+              error:
+                type: string
+              code:
+                type: integer
+                format: int32
+              message:
+                type: string
+              details:
+                type: array
+                items:
+                  type: object
+                  properties:
+                    type_url:
+                      type: string
+                    value:
+                      type: string
+                      format: byte
+      tags:
+        - Query
+  /greenfield/challenge/params:
+    get:
+      summary: Parameters queries the parameters of the module.
+      operationId: ChallengeParams
+      responses:
+        '200':
+          description: A successful response.
+          schema:
+            type: object
+            properties:
+              params:
+                description: params holds all the parameters of this module.
+                type: object
+                properties:
+                  challenge_count_per_block:
+                    type: string
+                    format: uint64
+                    description: >-
+                      Challenges which will be emitted in each block, including
+                      user submitted or randomly triggered.
+                  challenge_keep_alive_period:
+                    type: string
+                    format: uint64
+                    description: >-
+                      Challenges will be expired after the period, including
+                      user submitted or randomly triggered.
+                  slash_cooling_off_period:
+                    type: string
+                    format: uint64
+                    description: >-
+                      The count of blocks to stand for the period in which the
+                      same storage and object info cannot be slashed again.
+                  slash_amount_size_rate:
+                    type: string
+                    description: >-
+                      The slash coin amount will be calculated from the size of
+                      object info, and adjusted by this rate.
+                  slash_amount_min:
+                    type: string
+                    description: The minimal slash amount.
+                  slash_amount_max:
+                    type: string
+                    description: The maximum slash amount.
+                  reward_validator_ratio:
+                    type: string
+                    description: >-
+                      The ratio of slash amount to reward all current
+                      validators.
+                  reward_submitter_ratio:
+                    type: string
+                    description: >-
+                      The ratio of reward amount to reward attestation
+                      submitter.
+                  reward_submitter_threshold:
+                    type: string
+                    description: >-
+                      The reward amount to submitter will be adjusted by the
+                      threshold.
+                  heartbeat_interval:
+                    type: string
+                    format: uint64
+                    description: >-
+                      Heartbeat interval, based on challenge id, defines the
+                      frequency of heartbeat attestation.
+                  attestation_inturn_interval:
+                    type: string
+                    format: uint64
+                    description: >-
+                      The time duration for each submitter to submit
+                      attestations in turn.
+                  attestation_kept_count:
+                    type: string
+                    format: uint64
+                    description: >-
+                      The number of kept attested challenge ids, which can be
+                      queried by clients.
+            description: >-
+              QueryParamsResponse is response type for the Query/Params RPC
+              method.
+        default:
+          description: An unexpected error response.
+          schema:
+            type: object
+            properties:
+              error:
+                type: string
+              code:
+                type: integer
+                format: int32
+              message:
+                type: string
+              details:
+                type: array
+                items:
+                  type: object
+                  properties:
+                    type_url:
+                      type: string
+                    value:
+                      type: string
+                      format: byte
+      tags:
+        - Query
   /greenfield/payment/auto_settle_record:
     get:
       summary: Queries a list of AutoSettleRecord items.
@@ -266,25 +474,15 @@ paths:
                     type: string
                     format: int64
                     title: the unix timestamp when the stream account will be settled
-                  out_flows:
-                    type: array
-                    items:
-                      type: object
-                      properties:
-                        to_address:
-                          type: string
-                          title: >-
-                            stream account address who receives the flow,
-                            usually SP(service provider)
-                        rate:
-                          type: string
-                          title: flow rate
-                      title: >-
-                        OutFlow defines the accumulative outflow stream rate in
-                        BNB
-
-                        from a stream account to a Storage Provider
-                    title: the accumulated outflow rates of the stream account
+                  out_flow_count:
+                    type: string
+                    format: uint64
+                    title: the count of its out flows
+                  frozen_netflow_rate:
+                    type: string
+                    title: >-
+                      the frozen netflow rate, which is used when resuming
+                      stream account
               current_timestamp:
                 type: string
                 format: int64
@@ -373,6 +571,72 @@ paths:
           type: string
       tags:
         - Query
+  /greenfield/payment/out_flows/{account}:
+    get:
+      summary: Queries a StreamRecord by index.
+      operationId: OutFlows
+      responses:
+        '200':
+          description: A successful response.
+          schema:
+            type: object
+            properties:
+              out_flows:
+                type: array
+                items:
+                  type: object
+                  properties:
+                    to_address:
+                      type: string
+                      title: >-
+                        stream account address who receives the flow, usually
+                        SP(service provider)
+                    rate:
+                      type: string
+                      title: flow rate
+                    status:
+                      title: status
+                      type: string
+                      enum:
+                        - OUT_FLOW_STATUS_ACTIVE
+                        - OUT_FLOW_STATUS_FROZEN
+                      default: OUT_FLOW_STATUS_ACTIVE
+                      description: >-
+                        - OUT_FLOW_STATUS_ACTIVE: OUT_FLOW_STATUS_ACTIVE defines
+                        the active status of a out flow.
+                         - OUT_FLOW_STATUS_FROZEN: OUT_FLOW_STATUS_FROZEN defines the frozen status of a out flow.
+                  title: |-
+                    OutFlow defines the accumulative outflow stream rate in BNB
+                    from a stream account to a Storage Provider
+        default:
+          description: An unexpected error response.
+          schema:
+            type: object
+            properties:
+              error:
+                type: string
+              code:
+                type: integer
+                format: int32
+              message:
+                type: string
+              details:
+                type: array
+                items:
+                  type: object
+                  properties:
+                    type_url:
+                      type: string
+                    value:
+                      type: string
+                      format: byte
+      parameters:
+        - name: account
+          in: path
+          required: true
+          type: string
+      tags:
+        - Query
   /greenfield/payment/params:
     get:
       summary: Parameters queries the parameters of the module.
@@ -387,12 +651,110 @@ paths:
                 description: params holds all the parameters of this module.
                 type: object
                 properties:
-                  reserve_time:
+                  versioned_params:
+                    type: object
+                    properties:
+                      reserve_time:
+                        type: string
+                        format: uint64
+                        title: >-
+                          Time duration which the buffer balance need to be
+                          reserved for NetOutFlow e.g. 6 month
+                      validator_tax_rate:
+                        type: string
+                        title: >-
+                          The tax rate to pay for validators in storage payment.
+                          The default value is 1%(0.01)
+                    description: >-
+                      VersionedParams defines the parameters with multiple
+                      versions, each version is stored with different timestamp.
+                  payment_account_count_limit:
+                    type: string
+                    format: uint64
+                    title: >-
+                      The maximum number of payment accounts that can be created
+                      by one user
+                  forced_settle_time:
+                    type: string
+                    format: uint64
+                    description: >-
+                      Time duration threshold of forced settlement.
+
+                      If dynamic balance is less than NetOutFlowRate *
+                      forcedSettleTime, the account can be forced settled.
+                  max_auto_settle_flow_count:
                     type: string
                     format: uint64
                     title: >-
-                      Time duration which the buffer balance need to be reserved
-                      for NetOutFlow e.g. 6 month
+                      the maximum number of flows that will be auto forced
+                      settled in one block
+                  max_auto_resume_flow_count:
+                    type: string
+                    format: uint64
+                    title: >-
+                      the maximum number of flows that will be auto resumed in
+                      one block
+                  fee_denom:
+                    type: string
+                    title: The denom of fee charged in payment module
+            description: >-
+              QueryParamsResponse is response type for the Query/Params RPC
+              method.
+        default:
+          description: An unexpected error response.
+          schema:
+            type: object
+            properties:
+              error:
+                type: string
+              code:
+                type: integer
+                format: int32
+              message:
+                type: string
+              details:
+                type: array
+                items:
+                  type: object
+                  properties:
+                    type_url:
+                      type: string
+                    value:
+                      type: string
+                      format: byte
+      tags:
+        - Query
+  /greenfield/payment/params/{timestamp}:
+    get:
+      summary: ParamsByTimestamp queries the parameters of the module.
+      operationId: ParamsByTimestamp
+      responses:
+        '200':
+          description: A successful response.
+          schema:
+            type: object
+            properties:
+              params:
+                description: params holds all the parameters of this module.
+                type: object
+                properties:
+                  versioned_params:
+                    type: object
+                    properties:
+                      reserve_time:
+                        type: string
+                        format: uint64
+                        title: >-
+                          Time duration which the buffer balance need to be
+                          reserved for NetOutFlow e.g. 6 month
+                      validator_tax_rate:
+                        type: string
+                        title: >-
+                          The tax rate to pay for validators in storage payment.
+                          The default value is 1%(0.01)
+                    description: >-
+                      VersionedParams defines the parameters with multiple
+                      versions, each version is stored with different timestamp.
                   payment_account_count_limit:
                     type: string
                     format: uint64
@@ -407,23 +769,24 @@ paths:
 
                       If dynamic balance is less than NetOutFlowRate *
                       forcedSettleTime, the account can be forced settled.
-                  max_auto_force_settle_num:
+                  max_auto_settle_flow_count:
                     type: string
                     format: uint64
                     title: >-
-                      the maximum number of accounts that will be forced settled
-                      in one block
+                      the maximum number of flows that will be auto forced
+                      settled in one block
+                  max_auto_resume_flow_count:
+                    type: string
+                    format: uint64
+                    title: >-
+                      the maximum number of flows that will be auto resumed in
+                      one block
                   fee_denom:
                     type: string
                     title: The denom of fee charged in payment module
-                  validator_tax_rate:
-                    type: string
-                    title: >-
-                      The tax rate to pay for validators in storage payment. The
-                      default value is 1%(0.01)
             description: >-
-              QueryParamsResponse is response type for the Query/Params RPC
-              method.
+              QueryParamsByTimestampResponse is response type for the
+              Query/ParamsByTimestamp RPC method with timestamp.
         default:
           description: An unexpected error response.
           schema:
@@ -446,6 +809,13 @@ paths:
                     value:
                       type: string
                       format: byte
+      parameters:
+        - name: timestamp
+          description: the timestamp of the block time you want to query
+          in: path
+          required: true
+          type: string
+          format: int64
       tags:
         - Query
   /greenfield/payment/payment_account:
@@ -890,25 +1260,15 @@ paths:
                       title: >-
                         the unix timestamp when the stream account will be
                         settled
-                    out_flows:
-                      type: array
-                      items:
-                        type: object
-                        properties:
-                          to_address:
-                            type: string
-                            title: >-
-                              stream account address who receives the flow,
-                              usually SP(service provider)
-                          rate:
-                            type: string
-                            title: flow rate
-                        title: >-
-                          OutFlow defines the accumulative outflow stream rate
-                          in BNB
-
-                          from a stream account to a Storage Provider
-                      title: the accumulated outflow rates of the stream account
+                    out_flow_count:
+                      type: string
+                      format: uint64
+                      title: the count of its out flows
+                    frozen_netflow_rate:
+                      type: string
+                      title: >-
+                        the frozen netflow rate, which is used when resuming
+                        stream account
                   title: Stream Payment Record of a stream account
               pagination:
                 type: object
@@ -1084,25 +1444,15 @@ paths:
                     type: string
                     format: int64
                     title: the unix timestamp when the stream account will be settled
-                  out_flows:
-                    type: array
-                    items:
-                      type: object
-                      properties:
-                        to_address:
-                          type: string
-                          title: >-
-                            stream account address who receives the flow,
-                            usually SP(service provider)
-                        rate:
-                          type: string
-                          title: flow rate
-                      title: >-
-                        OutFlow defines the accumulative outflow stream rate in
-                        BNB
-
-                        from a stream account to a Storage Provider
-                    title: the accumulated outflow rates of the stream account
+                  out_flow_count:
+                    type: string
+                    format: uint64
+                    title: the count of its out flows
+                  frozen_netflow_rate:
+                    type: string
+                    title: >-
+                      the frozen netflow rate, which is used when resuming
+                      stream account
                 title: Stream Payment Record of a stream account
         default:
           description: An unexpected error response.
@@ -1202,9 +1552,10 @@ paths:
               sp_storage_price:
                 type: object
                 properties:
-                  sp_address:
-                    type: string
-                    title: sp address
+                  sp_id:
+                    type: integer
+                    format: int64
+                    title: sp id
                   update_time_sec:
                     type: string
                     format: int64
@@ -1312,9 +1663,140 @@ paths:
                       format: byte
       tags:
         - Query
-  /greenfield/storage_provider/{spAddress}:
+  /greenfield/sp/storage_provider_by_operator_address:
+    get:
+      summary: Queries a StorageProvider by specify operator address.
+      operationId: StorageProviderByOperatorAddress
+      responses:
+        '200':
+          description: A successful response.
+          schema:
+            type: object
+            properties:
+              storageProvider:
+                type: object
+                properties:
+                  id:
+                    type: integer
+                    format: int64
+                    title: >-
+                      // id is the identifier of the storage provider, used in
+                      virtual group
+                  operator_address:
+                    type: string
+                    description: >-
+                      operator_address defines the account address of the
+                      storage provider's operator; It also is the unique index
+                      key of sp.
+                  funding_address:
+                    type: string
+                    description: >-
+                      funding_address defines one of the storage provider's
+                      accounts which is used to deposit and reward.
+                  seal_address:
+                    type: string
+                    title: >-
+                      seal_address defines one of the storage provider's
+                      accounts which is used to SealObject
+                  approval_address:
+                    type: string
+                    title: >-
+                      approval_address defines one of the storage provider's
+                      accounts which is used to approve use's
+                      createBucket/createObject request
+                  gc_address:
+                    type: string
+                    description: >-
+                      gc_address defines one of the storage provider's accounts
+                      which is used for gc purpose.
+                  total_deposit:
+                    type: string
+                    description: >-
+                      total_deposit defines the number of tokens deposited by
+                      this storage provider for staking.
+                  status:
+                    title: >-
+                      status defines the current service status of this storage
+                      provider
+                    type: string
+                    enum:
+                      - STATUS_IN_SERVICE
+                      - STATUS_IN_JAILED
+                      - STATUS_GRACEFUL_EXITING
+                      - STATUS_OUT_OF_SERVICE
+                    default: STATUS_IN_SERVICE
+                    description: Status is the status of a storage provider.
+                  endpoint:
+                    type: string
+                    title: >-
+                      endpoint define the storage provider's network service
+                      address
+                  description:
+                    description: >-
+                      description defines the description terms for the storage
+                      provider.
+                    type: object
+                    properties:
+                      moniker:
+                        type: string
+                        title: >-
+                          moniker defines a human-readable name for the storage
+                          provider
+                      identity:
+                        type: string
+                        description: >-
+                          identity defines an optional identity signature (ex.
+                          UPort or Keybase).
+                      website:
+                        type: string
+                        description: website defines an optional website link.
+                      security_contact:
+                        type: string
+                        description: >-
+                          security_contact defines an optional email for
+                          security contact.
+                      details:
+                        type: string
+                        description: details define other optional details.
+                  bls_key:
+                    type: string
+                    format: byte
+                    title: >-
+                      bls_key defines the bls pub key of the Storage provider
+                      for sealing object and completing migration
+                title: StorageProvider defines the meta info of storage provider
+        default:
+          description: An unexpected error response.
+          schema:
+            type: object
+            properties:
+              error:
+                type: string
+              code:
+                type: integer
+                format: int32
+              message:
+                type: string
+              details:
+                type: array
+                items:
+                  type: object
+                  properties:
+                    type_url:
+                      type: string
+                    value:
+                      type: string
+                      format: byte
+      parameters:
+        - name: operator_address
+          in: query
+          required: false
+          type: string
+      tags:
+        - Query
+  /greenfield/storage_provider/{id}:
     get:
-      summary: Queries a storage provider with specify address
+      summary: Queries a storage provider with specify id
       operationId: StorageProvider
       responses:
         '200':
@@ -1325,6 +1807,12 @@ paths:
               storageProvider:
                 type: object
                 properties:
+                  id:
+                    type: integer
+                    format: int64
+                    title: >-
+                      // id is the identifier of the storage provider, used in
+                      virtual group
                   operator_address:
                     type: string
                     description: >-
@@ -1401,6 +1889,12 @@ paths:
                       details:
                         type: string
                         description: details define other optional details.
+                  bls_key:
+                    type: string
+                    format: byte
+                    title: >-
+                      bls_key defines the bls pub key of the Storage provider
+                      for sealing object and completing migration
                 title: StorageProvider defines the meta info of storage provider
         default:
           description: An unexpected error response.
@@ -1425,10 +1919,11 @@ paths:
                       type: string
                       format: byte
       parameters:
-        - name: spAddress
+        - name: id
           in: path
           required: true
-          type: string
+          type: integer
+          format: int64
       tags:
         - Query
   /greenfield/storage_providers:
@@ -1446,6 +1941,12 @@ paths:
                 items:
                   type: object
                   properties:
+                    id:
+                      type: integer
+                      format: int64
+                      title: >-
+                        // id is the identifier of the storage provider, used in
+                        virtual group
                     operator_address:
                       type: string
                       description: >-
@@ -1522,6 +2023,12 @@ paths:
                         details:
                           type: string
                           description: details define other optional details.
+                    bls_key:
+                      type: string
+                      format: byte
+                      title: >-
+                        bls_key defines the bls pub key of the Storage provider
+                        for sealing object and completing migration
                   title: StorageProvider defines the meta info of storage provider
               pagination:
                 description: pagination defines the pagination in the response.
@@ -1678,14 +2185,21 @@ paths:
                   payment_address:
                     type: string
                     title: payment_address is the address of the payment account
-                  primary_sp_address:
-                    type: string
+                  primary_sp_id:
+                    type: integer
+                    format: int64
                     description: >-
-                      primary_sp_address is the address of the primary sp.
-                      Objects belongs to this bucket will never
+                      primary_sp_id is the unique id of the primary sp. Objects
+                      belongs to this bucket will never
 
                       leave this SP, unless you explicitly shift them to another
                       SP.
+                  global_virtual_group_family_id:
+                    type: integer
+                    format: int64
+                    title: >-
+                      global_virtual_group_family_id defines the unique id of
+                      gvg family
                   charged_read_quota:
                     type: string
                     format: uint64
@@ -1697,44 +2211,13 @@ paths:
                       free read data provided by SP and
 
                       the ChargeReadQuota specified here.
-                  billing_info:
-                    title: billing info of the bucket
-                    type: object
-                    properties:
-                      price_time:
-                        type: string
-                        format: int64
-                        title: >-
-                          the time of the payment price, used to calculate the
-                          charge rate of the bucket
-                      total_charge_size:
-                        type: string
-                        format: uint64
-                        title: >-
-                          the total size of the objects in the bucket, used to
-                          calculate the charge rate of the bucket
-                      secondary_sp_objects_size:
-                        type: array
-                        items:
-                          type: object
-                          properties:
-                            sp_address:
-                              type: string
-                              title: address is the address of the secondary sp
-                            total_charge_size:
-                              type: string
-                              format: uint64
-                              title: >-
-                                size is the total size of the objects in the
-                                secondary sp
-                          title: secondary sp objects size statistics
-                        title: secondary sp objects size statistics
                   bucket_status:
                     description: bucket_status define the status of the bucket.
                     type: string
                     enum:
                       - BUCKET_STATUS_CREATED
                       - BUCKET_STATUS_DISCONTINUED
+                      - BUCKET_STATUS_MIGRATING
                     default: BUCKET_STATUS_CREATED
         default:
           description: An unexpected error response.
@@ -1820,14 +2303,21 @@ paths:
                   payment_address:
                     type: string
                     title: payment_address is the address of the payment account
-                  primary_sp_address:
-                    type: string
+                  primary_sp_id:
+                    type: integer
+                    format: int64
                     description: >-
-                      primary_sp_address is the address of the primary sp.
-                      Objects belongs to this bucket will never
+                      primary_sp_id is the unique id of the primary sp. Objects
+                      belongs to this bucket will never
 
                       leave this SP, unless you explicitly shift them to another
                       SP.
+                  global_virtual_group_family_id:
+                    type: integer
+                    format: int64
+                    title: >-
+                      global_virtual_group_family_id defines the unique id of
+                      gvg family
                   charged_read_quota:
                     type: string
                     format: uint64
@@ -1839,44 +2329,13 @@ paths:
                       free read data provided by SP and
 
                       the ChargeReadQuota specified here.
-                  billing_info:
-                    title: billing info of the bucket
-                    type: object
-                    properties:
-                      price_time:
-                        type: string
-                        format: int64
-                        title: >-
-                          the time of the payment price, used to calculate the
-                          charge rate of the bucket
-                      total_charge_size:
-                        type: string
-                        format: uint64
-                        title: >-
-                          the total size of the objects in the bucket, used to
-                          calculate the charge rate of the bucket
-                      secondary_sp_objects_size:
-                        type: array
-                        items:
-                          type: object
-                          properties:
-                            sp_address:
-                              type: string
-                              title: address is the address of the secondary sp
-                            total_charge_size:
-                              type: string
-                              format: uint64
-                              title: >-
-                                size is the total size of the objects in the
-                                secondary sp
-                          title: secondary sp objects size statistics
-                        title: secondary sp objects size statistics
                   bucket_status:
                     description: bucket_status define the status of the bucket.
                     type: string
                     enum:
                       - BUCKET_STATUS_CREATED
                       - BUCKET_STATUS_DISCONTINUED
+                      - BUCKET_STATUS_MIGRATING
                     default: BUCKET_STATUS_CREATED
         default:
           description: An unexpected error response.
@@ -2181,6 +2640,12 @@ paths:
                 properties:
                   owner:
                     type: string
+                    title: owner is the object owner
+                  creator:
+                    type: string
+                    title: >-
+                      creator is the address of the uploader, it always be same
+                      as owner address
                   bucket_name:
                     type: string
                     title: bucket_name is the name of the bucket
@@ -2190,6 +2655,9 @@ paths:
                   id:
                     type: string
                     title: id is the unique identifier of object
+                  local_virtual_group_id:
+                    type: integer
+                    format: int64
                   payload_size:
                     type: string
                     format: uint64
@@ -2258,13 +2726,6 @@ paths:
 
                       add omit tag to omit the field when converting to NFT
                       metadata
-                  secondary_sp_addresses:
-                    type: array
-                    items:
-                      type: string
-                    title: >-
-                      secondary_sp_addresses define the addresses of
-                      secondary_sps
         default:
           description: An unexpected error response.
           schema:
@@ -2313,6 +2774,12 @@ paths:
                 properties:
                   owner:
                     type: string
+                    title: owner is the object owner
+                  creator:
+                    type: string
+                    title: >-
+                      creator is the address of the uploader, it always be same
+                      as owner address
                   bucket_name:
                     type: string
                     title: bucket_name is the name of the bucket
@@ -2322,6 +2789,9 @@ paths:
                   id:
                     type: string
                     title: id is the unique identifier of object
+                  local_virtual_group_id:
+                    type: integer
+                    format: int64
                   payload_size:
                     type: string
                     format: uint64
@@ -2390,13 +2860,6 @@ paths:
 
                       add omit tag to omit the field when converting to NFT
                       metadata
-                  secondary_sp_addresses:
-                    type: array
-                    items:
-                      type: string
-                    title: >-
-                      secondary_sp_addresses define the addresses of
-                      secondary_sps
         default:
           description: An unexpected error response.
           schema:
@@ -2547,14 +3010,21 @@ paths:
                     payment_address:
                       type: string
                       title: payment_address is the address of the payment account
-                    primary_sp_address:
-                      type: string
+                    primary_sp_id:
+                      type: integer
+                      format: int64
                       description: >-
-                        primary_sp_address is the address of the primary sp.
+                        primary_sp_id is the unique id of the primary sp.
                         Objects belongs to this bucket will never
 
                         leave this SP, unless you explicitly shift them to
                         another SP.
+                    global_virtual_group_family_id:
+                      type: integer
+                      format: int64
+                      title: >-
+                        global_virtual_group_family_id defines the unique id of
+                        gvg family
                     charged_read_quota:
                       type: string
                       format: uint64
@@ -2566,44 +3036,13 @@ paths:
                         free read data provided by SP and
 
                         the ChargeReadQuota specified here.
-                    billing_info:
-                      title: billing info of the bucket
-                      type: object
-                      properties:
-                        price_time:
-                          type: string
-                          format: int64
-                          title: >-
-                            the time of the payment price, used to calculate the
-                            charge rate of the bucket
-                        total_charge_size:
-                          type: string
-                          format: uint64
-                          title: >-
-                            the total size of the objects in the bucket, used to
-                            calculate the charge rate of the bucket
-                        secondary_sp_objects_size:
-                          type: array
-                          items:
-                            type: object
-                            properties:
-                              sp_address:
-                                type: string
-                                title: address is the address of the secondary sp
-                              total_charge_size:
-                                type: string
-                                format: uint64
-                                title: >-
-                                  size is the total size of the objects in the
-                                  secondary sp
-                            title: secondary sp objects size statistics
-                          title: secondary sp objects size statistics
                     bucket_status:
                       description: bucket_status define the status of the bucket.
                       type: string
                       enum:
                         - BUCKET_STATUS_CREATED
                         - BUCKET_STATUS_DISCONTINUED
+                        - BUCKET_STATUS_MIGRATING
                       default: BUCKET_STATUS_CREATED
               pagination:
                 type: object
@@ -2883,6 +3322,12 @@ paths:
                   properties:
                     owner:
                       type: string
+                      title: owner is the object owner
+                    creator:
+                      type: string
+                      title: >-
+                        creator is the address of the uploader, it always be
+                        same as owner address
                     bucket_name:
                       type: string
                       title: bucket_name is the name of the bucket
@@ -2892,6 +3337,9 @@ paths:
                     id:
                       type: string
                       title: id is the unique identifier of object
+                    local_virtual_group_id:
+                      type: integer
+                      format: int64
                     payload_size:
                       type: string
                       format: uint64
@@ -2960,13 +3408,6 @@ paths:
 
                         add omit tag to omit the field when converting to NFT
                         metadata
-                    secondary_sp_addresses:
-                      type: array
-                      items:
-                        type: string
-                      title: >-
-                        secondary_sp_addresses define the addresses of
-                        secondary_sps
               pagination:
                 type: object
                 properties:
@@ -3097,6 +3538,12 @@ paths:
                   properties:
                     owner:
                       type: string
+                      title: owner is the object owner
+                    creator:
+                      type: string
+                      title: >-
+                        creator is the address of the uploader, it always be
+                        same as owner address
                     bucket_name:
                       type: string
                       title: bucket_name is the name of the bucket
@@ -3106,6 +3553,9 @@ paths:
                     id:
                       type: string
                       title: id is the unique identifier of object
+                    local_virtual_group_id:
+                      type: integer
+                      format: int64
                     payload_size:
                       type: string
                       format: uint64
@@ -3174,13 +3624,6 @@ paths:
 
                         add omit tag to omit the field when converting to NFT
                         metadata
-                    secondary_sp_addresses:
-                      type: array
-                      items:
-                        type: string
-                      title: >-
-                        secondary_sp_addresses define the addresses of
-                        secondary_sps
               pagination:
                 type: object
                 properties:
@@ -3402,6 +3845,14 @@ paths:
                     type: string
                     format: uint64
                     title: The max number for deleting policy in each end block
+                  min_quota_update_interval:
+                    type: string
+                    format: uint64
+                    title: The min interval for making quota smaller in seconds
+                  max_local_virtual_group_num_per_bucket:
+                    type: integer
+                    format: int64
+                    title: the max number of local virtual group per bucket
             description: >-
               QueryParamsResponse is response type for the Query/Params RPC
               method.
@@ -3537,6 +3988,14 @@ paths:
                     type: string
                     format: uint64
                     title: The max number for deleting policy in each end block
+                  min_quota_update_interval:
+                    type: string
+                    format: uint64
+                    title: The min interval for making quota smaller in seconds
+                  max_local_virtual_group_num_per_bucket:
+                    type: integer
+                    format: int64
+                    title: the max number of local virtual group per bucket
             description: >-
               QueryVersionedParamsResponse is response type for the Query/Params
               RPC method with timestamp.
@@ -29775,6 +30234,209 @@ definitions:
                   "@type": "type.googleapis.com/google.protobuf.Duration",
                   "value": "1.212s"
                 }
+  greenfield.challenge.AttestedChallenge:
+    type: object
+    properties:
+      id:
+        type: string
+        format: uint64
+        description: The id of the challenge.
+      result:
+        description: The attestation result of the challenge.
+        type: string
+        enum:
+          - CHALLENGE_FAILED
+          - CHALLENGE_SUCCEED
+        default: CHALLENGE_FAILED
+    description: AttestedChallenge records the challenge which are attested.
+  greenfield.challenge.Params:
+    type: object
+    properties:
+      challenge_count_per_block:
+        type: string
+        format: uint64
+        description: >-
+          Challenges which will be emitted in each block, including user
+          submitted or randomly triggered.
+      challenge_keep_alive_period:
+        type: string
+        format: uint64
+        description: >-
+          Challenges will be expired after the period, including user submitted
+          or randomly triggered.
+      slash_cooling_off_period:
+        type: string
+        format: uint64
+        description: >-
+          The count of blocks to stand for the period in which the same storage
+          and object info cannot be slashed again.
+      slash_amount_size_rate:
+        type: string
+        description: >-
+          The slash coin amount will be calculated from the size of object info,
+          and adjusted by this rate.
+      slash_amount_min:
+        type: string
+        description: The minimal slash amount.
+      slash_amount_max:
+        type: string
+        description: The maximum slash amount.
+      reward_validator_ratio:
+        type: string
+        description: The ratio of slash amount to reward all current validators.
+      reward_submitter_ratio:
+        type: string
+        description: The ratio of reward amount to reward attestation submitter.
+      reward_submitter_threshold:
+        type: string
+        description: The reward amount to submitter will be adjusted by the threshold.
+      heartbeat_interval:
+        type: string
+        format: uint64
+        description: >-
+          Heartbeat interval, based on challenge id, defines the frequency of
+          heartbeat attestation.
+      attestation_inturn_interval:
+        type: string
+        format: uint64
+        description: The time duration for each submitter to submit attestations in turn.
+      attestation_kept_count:
+        type: string
+        format: uint64
+        description: >-
+          The number of kept attested challenge ids, which can be queried by
+          clients.
+    description: Params defines the parameters for the module.
+  greenfield.challenge.QueryInturnAttestationSubmitterResponse:
+    type: object
+    properties:
+      bls_pub_key:
+        type: string
+      submit_interval:
+        type: object
+        properties:
+          start:
+            type: string
+            format: uint64
+          end:
+            type: string
+            format: uint64
+        description: >-
+          SubmitInterval holds start and end (exclusive) (i.e., [start, end))
+          time of in turn attestation.
+    description: >-
+      QueryInturnAttestationSubmitterResponse is response type for the
+      Query/InturnAttestationSubmitter RPC method.
+  greenfield.challenge.QueryLatestAttestedChallengesResponse:
+    type: object
+    properties:
+      challenges:
+        type: array
+        items:
+          type: object
+          properties:
+            id:
+              type: string
+              format: uint64
+              description: The id of the challenge.
+            result:
+              description: The attestation result of the challenge.
+              type: string
+              enum:
+                - CHALLENGE_FAILED
+                - CHALLENGE_SUCCEED
+              default: CHALLENGE_FAILED
+          description: AttestedChallenge records the challenge which are attested.
+    description: >-
+      QueryLatestAttestedChallengesResponse is response type for the
+      Query/LatestAttestedChallenges RPC method.
+  greenfield.challenge.QueryParamsResponse:
+    type: object
+    properties:
+      params:
+        description: params holds all the parameters of this module.
+        type: object
+        properties:
+          challenge_count_per_block:
+            type: string
+            format: uint64
+            description: >-
+              Challenges which will be emitted in each block, including user
+              submitted or randomly triggered.
+          challenge_keep_alive_period:
+            type: string
+            format: uint64
+            description: >-
+              Challenges will be expired after the period, including user
+              submitted or randomly triggered.
+          slash_cooling_off_period:
+            type: string
+            format: uint64
+            description: >-
+              The count of blocks to stand for the period in which the same
+              storage and object info cannot be slashed again.
+          slash_amount_size_rate:
+            type: string
+            description: >-
+              The slash coin amount will be calculated from the size of object
+              info, and adjusted by this rate.
+          slash_amount_min:
+            type: string
+            description: The minimal slash amount.
+          slash_amount_max:
+            type: string
+            description: The maximum slash amount.
+          reward_validator_ratio:
+            type: string
+            description: The ratio of slash amount to reward all current validators.
+          reward_submitter_ratio:
+            type: string
+            description: The ratio of reward amount to reward attestation submitter.
+          reward_submitter_threshold:
+            type: string
+            description: The reward amount to submitter will be adjusted by the threshold.
+          heartbeat_interval:
+            type: string
+            format: uint64
+            description: >-
+              Heartbeat interval, based on challenge id, defines the frequency
+              of heartbeat attestation.
+          attestation_inturn_interval:
+            type: string
+            format: uint64
+            description: >-
+              The time duration for each submitter to submit attestations in
+              turn.
+          attestation_kept_count:
+            type: string
+            format: uint64
+            description: >-
+              The number of kept attested challenge ids, which can be queried by
+              clients.
+    description: QueryParamsResponse is response type for the Query/Params RPC method.
+  greenfield.challenge.SubmitInterval:
+    type: object
+    properties:
+      start:
+        type: string
+        format: uint64
+      end:
+        type: string
+        format: uint64
+    description: >-
+      SubmitInterval holds start and end (exclusive) (i.e., [start, end)) time
+      of in turn attestation.
+  greenfield.challenge.VoteResult:
+    type: string
+    enum:
+      - CHALLENGE_FAILED
+      - CHALLENGE_SUCCEED
+    default: CHALLENGE_FAILED
+    description: |-
+      VoteResult defines the result attestation for a challenge.
+
+       - CHALLENGE_FAILED: The challenge failed.
+       - CHALLENGE_SUCCEED: The challenge succeed.
   cosmos.base.query.v1beta1.PageRequest:
     type: object
     properties:
@@ -29882,18 +30544,51 @@ definitions:
       rate:
         type: string
         title: flow rate
+      status:
+        title: status
+        type: string
+        enum:
+          - OUT_FLOW_STATUS_ACTIVE
+          - OUT_FLOW_STATUS_FROZEN
+        default: OUT_FLOW_STATUS_ACTIVE
+        description: >-
+          - OUT_FLOW_STATUS_ACTIVE: OUT_FLOW_STATUS_ACTIVE defines the active
+          status of a out flow.
+           - OUT_FLOW_STATUS_FROZEN: OUT_FLOW_STATUS_FROZEN defines the frozen status of a out flow.
     title: |-
       OutFlow defines the accumulative outflow stream rate in BNB
       from a stream account to a Storage Provider
+  greenfield.payment.OutFlowStatus:
+    type: string
+    enum:
+      - OUT_FLOW_STATUS_ACTIVE
+      - OUT_FLOW_STATUS_FROZEN
+    default: OUT_FLOW_STATUS_ACTIVE
+    description: >-
+      - OUT_FLOW_STATUS_ACTIVE: OUT_FLOW_STATUS_ACTIVE defines the active status
+      of a out flow.
+       - OUT_FLOW_STATUS_FROZEN: OUT_FLOW_STATUS_FROZEN defines the frozen status of a out flow.
+    title: OutFlowStatus defines the status of a out flow
   greenfield.payment.Params:
     type: object
     properties:
-      reserve_time:
-        type: string
-        format: uint64
-        title: >-
-          Time duration which the buffer balance need to be reserved for
-          NetOutFlow e.g. 6 month
+      versioned_params:
+        type: object
+        properties:
+          reserve_time:
+            type: string
+            format: uint64
+            title: >-
+              Time duration which the buffer balance need to be reserved for
+              NetOutFlow e.g. 6 month
+          validator_tax_rate:
+            type: string
+            title: >-
+              The tax rate to pay for validators in storage payment. The default
+              value is 1%(0.01)
+        description: >-
+          VersionedParams defines the parameters with multiple versions, each
+          version is stored with different timestamp.
       payment_account_count_limit:
         type: string
         format: uint64
@@ -29906,20 +30601,19 @@ definitions:
 
           If dynamic balance is less than NetOutFlowRate * forcedSettleTime, the
           account can be forced settled.
-      max_auto_force_settle_num:
+      max_auto_settle_flow_count:
         type: string
         format: uint64
         title: >-
-          the maximum number of accounts that will be forced settled in one
+          the maximum number of flows that will be auto forced settled in one
           block
+      max_auto_resume_flow_count:
+        type: string
+        format: uint64
+        title: the maximum number of flows that will be auto resumed in one block
       fee_denom:
         type: string
         title: The denom of fee charged in payment module
-      validator_tax_rate:
-        type: string
-        title: >-
-          The tax rate to pay for validators in storage payment. The default
-          value is 1%(0.01)
     description: Params defines the parameters for the module.
   greenfield.payment.PaymentAccount:
     type: object
@@ -30140,23 +30834,15 @@ definitions:
               type: string
               format: int64
               title: the unix timestamp when the stream account will be settled
-            out_flows:
-              type: array
-              items:
-                type: object
-                properties:
-                  to_address:
-                    type: string
-                    title: >-
-                      stream account address who receives the flow, usually
-                      SP(service provider)
-                  rate:
-                    type: string
-                    title: flow rate
-                title: |-
-                  OutFlow defines the accumulative outflow stream rate in BNB
-                  from a stream account to a Storage Provider
-              title: the accumulated outflow rates of the stream account
+            out_flow_count:
+              type: string
+              format: uint64
+              title: the count of its out flows
+            frozen_netflow_rate:
+              type: string
+              title: >-
+                the frozen netflow rate, which is used when resuming stream
+                account
           title: Stream Payment Record of a stream account
       pagination:
         type: object
@@ -30242,23 +30928,15 @@ definitions:
             type: string
             format: int64
             title: the unix timestamp when the stream account will be settled
-          out_flows:
-            type: array
-            items:
-              type: object
-              properties:
-                to_address:
-                  type: string
-                  title: >-
-                    stream account address who receives the flow, usually
-                    SP(service provider)
-                rate:
-                  type: string
-                  title: flow rate
-              title: |-
-                OutFlow defines the accumulative outflow stream rate in BNB
-                from a stream account to a Storage Provider
-            title: the accumulated outflow rates of the stream account
+          out_flow_count:
+            type: string
+            format: uint64
+            title: the count of its out flows
+          frozen_netflow_rate:
+            type: string
+            title: >-
+              the frozen netflow rate, which is used when resuming stream
+              account
       current_timestamp:
         type: string
         format: int64
@@ -30366,37 +31044,124 @@ definitions:
             type: string
             format: int64
             title: the unix timestamp when the stream account will be settled
-          out_flows:
-            type: array
-            items:
-              type: object
-              properties:
-                to_address:
-                  type: string
-                  title: >-
-                    stream account address who receives the flow, usually
-                    SP(service provider)
-                rate:
-                  type: string
-                  title: flow rate
-              title: |-
-                OutFlow defines the accumulative outflow stream rate in BNB
-                from a stream account to a Storage Provider
-            title: the accumulated outflow rates of the stream account
+          out_flow_count:
+            type: string
+            format: uint64
+            title: the count of its out flows
+          frozen_netflow_rate:
+            type: string
+            title: >-
+              the frozen netflow rate, which is used when resuming stream
+              account
         title: Stream Payment Record of a stream account
-  greenfield.payment.QueryParamsResponse:
+  greenfield.payment.QueryOutFlowsResponse:
+    type: object
+    properties:
+      out_flows:
+        type: array
+        items:
+          type: object
+          properties:
+            to_address:
+              type: string
+              title: >-
+                stream account address who receives the flow, usually SP(service
+                provider)
+            rate:
+              type: string
+              title: flow rate
+            status:
+              title: status
+              type: string
+              enum:
+                - OUT_FLOW_STATUS_ACTIVE
+                - OUT_FLOW_STATUS_FROZEN
+              default: OUT_FLOW_STATUS_ACTIVE
+              description: >-
+                - OUT_FLOW_STATUS_ACTIVE: OUT_FLOW_STATUS_ACTIVE defines the
+                active status of a out flow.
+                 - OUT_FLOW_STATUS_FROZEN: OUT_FLOW_STATUS_FROZEN defines the frozen status of a out flow.
+          title: |-
+            OutFlow defines the accumulative outflow stream rate in BNB
+            from a stream account to a Storage Provider
+  greenfield.payment.QueryParamsByTimestampResponse:
     type: object
     properties:
       params:
         description: params holds all the parameters of this module.
         type: object
         properties:
-          reserve_time:
+          versioned_params:
+            type: object
+            properties:
+              reserve_time:
+                type: string
+                format: uint64
+                title: >-
+                  Time duration which the buffer balance need to be reserved for
+                  NetOutFlow e.g. 6 month
+              validator_tax_rate:
+                type: string
+                title: >-
+                  The tax rate to pay for validators in storage payment. The
+                  default value is 1%(0.01)
+            description: >-
+              VersionedParams defines the parameters with multiple versions,
+              each version is stored with different timestamp.
+          payment_account_count_limit:
             type: string
             format: uint64
             title: >-
-              Time duration which the buffer balance need to be reserved for
-              NetOutFlow e.g. 6 month
+              The maximum number of payment accounts that can be created by one
+              user
+          forced_settle_time:
+            type: string
+            format: uint64
+            description: >-
+              Time duration threshold of forced settlement.
+
+              If dynamic balance is less than NetOutFlowRate * forcedSettleTime,
+              the account can be forced settled.
+          max_auto_settle_flow_count:
+            type: string
+            format: uint64
+            title: >-
+              the maximum number of flows that will be auto forced settled in
+              one block
+          max_auto_resume_flow_count:
+            type: string
+            format: uint64
+            title: the maximum number of flows that will be auto resumed in one block
+          fee_denom:
+            type: string
+            title: The denom of fee charged in payment module
+    description: >-
+      QueryParamsByTimestampResponse is response type for the
+      Query/ParamsByTimestamp RPC method with timestamp.
+  greenfield.payment.QueryParamsResponse:
+    type: object
+    properties:
+      params:
+        description: params holds all the parameters of this module.
+        type: object
+        properties:
+          versioned_params:
+            type: object
+            properties:
+              reserve_time:
+                type: string
+                format: uint64
+                title: >-
+                  Time duration which the buffer balance need to be reserved for
+                  NetOutFlow e.g. 6 month
+              validator_tax_rate:
+                type: string
+                title: >-
+                  The tax rate to pay for validators in storage payment. The
+                  default value is 1%(0.01)
+            description: >-
+              VersionedParams defines the parameters with multiple versions,
+              each version is stored with different timestamp.
           payment_account_count_limit:
             type: string
             format: uint64
@@ -30411,20 +31176,19 @@ definitions:
 
               If dynamic balance is less than NetOutFlowRate * forcedSettleTime,
               the account can be forced settled.
-          max_auto_force_settle_num:
+          max_auto_settle_flow_count:
             type: string
             format: uint64
             title: >-
-              the maximum number of accounts that will be forced settled in one
-              block
+              the maximum number of flows that will be auto forced settled in
+              one block
+          max_auto_resume_flow_count:
+            type: string
+            format: uint64
+            title: the maximum number of flows that will be auto resumed in one block
           fee_denom:
             type: string
             title: The denom of fee charged in payment module
-          validator_tax_rate:
-            type: string
-            title: >-
-              The tax rate to pay for validators in storage payment. The default
-              value is 1%(0.01)
     description: QueryParamsResponse is response type for the Query/Params RPC method.
   greenfield.payment.StreamAccountStatus:
     type: string
@@ -30488,24 +31252,31 @@ definitions:
         type: string
         format: int64
         title: the unix timestamp when the stream account will be settled
-      out_flows:
-        type: array
-        items:
-          type: object
-          properties:
-            to_address:
-              type: string
-              title: >-
-                stream account address who receives the flow, usually SP(service
-                provider)
-            rate:
-              type: string
-              title: flow rate
-          title: |-
-            OutFlow defines the accumulative outflow stream rate in BNB
-            from a stream account to a Storage Provider
-        title: the accumulated outflow rates of the stream account
+      out_flow_count:
+        type: string
+        format: uint64
+        title: the count of its out flows
+      frozen_netflow_rate:
+        type: string
+        title: the frozen netflow rate, which is used when resuming stream account
     title: Stream Payment Record of a stream account
+  greenfield.payment.VersionedParams:
+    type: object
+    properties:
+      reserve_time:
+        type: string
+        format: uint64
+        title: >-
+          Time duration which the buffer balance need to be reserved for
+          NetOutFlow e.g. 6 month
+      validator_tax_rate:
+        type: string
+        title: >-
+          The tax rate to pay for validators in storage payment. The default
+          value is 1%(0.01)
+    description: >-
+      VersionedParams defines the parameters with multiple versions, each
+      version is stored with different timestamp.
   greenfield.sp.Description:
     type: object
     properties:
@@ -30562,9 +31333,10 @@ definitions:
       sp_storage_price:
         type: object
         properties:
-          sp_address:
-            type: string
-            title: sp address
+          sp_id:
+            type: integer
+            format: int64
+            title: sp id
           update_time_sec:
             type: string
             format: int64
@@ -30601,12 +31373,105 @@ definitions:
               the ratio of the store price of the secondary sp to the primary
               sp, the default value is 80%
     description: QueryParamsResponse is response type for the Query/Params RPC method.
+  greenfield.sp.QueryStorageProviderByOperatorAddressResponse:
+    type: object
+    properties:
+      storageProvider:
+        type: object
+        properties:
+          id:
+            type: integer
+            format: int64
+            title: >-
+              // id is the identifier of the storage provider, used in virtual
+              group
+          operator_address:
+            type: string
+            description: >-
+              operator_address defines the account address of the storage
+              provider's operator; It also is the unique index key of sp.
+          funding_address:
+            type: string
+            description: >-
+              funding_address defines one of the storage provider's accounts
+              which is used to deposit and reward.
+          seal_address:
+            type: string
+            title: >-
+              seal_address defines one of the storage provider's accounts which
+              is used to SealObject
+          approval_address:
+            type: string
+            title: >-
+              approval_address defines one of the storage provider's accounts
+              which is used to approve use's createBucket/createObject request
+          gc_address:
+            type: string
+            description: >-
+              gc_address defines one of the storage provider's accounts which is
+              used for gc purpose.
+          total_deposit:
+            type: string
+            description: >-
+              total_deposit defines the number of tokens deposited by this
+              storage provider for staking.
+          status:
+            title: status defines the current service status of this storage provider
+            type: string
+            enum:
+              - STATUS_IN_SERVICE
+              - STATUS_IN_JAILED
+              - STATUS_GRACEFUL_EXITING
+              - STATUS_OUT_OF_SERVICE
+            default: STATUS_IN_SERVICE
+            description: Status is the status of a storage provider.
+          endpoint:
+            type: string
+            title: endpoint define the storage provider's network service address
+          description:
+            description: >-
+              description defines the description terms for the storage
+              provider.
+            type: object
+            properties:
+              moniker:
+                type: string
+                title: moniker defines a human-readable name for the storage provider
+              identity:
+                type: string
+                description: >-
+                  identity defines an optional identity signature (ex. UPort or
+                  Keybase).
+              website:
+                type: string
+                description: website defines an optional website link.
+              security_contact:
+                type: string
+                description: >-
+                  security_contact defines an optional email for security
+                  contact.
+              details:
+                type: string
+                description: details define other optional details.
+          bls_key:
+            type: string
+            format: byte
+            title: >-
+              bls_key defines the bls pub key of the Storage provider for
+              sealing object and completing migration
+        title: StorageProvider defines the meta info of storage provider
   greenfield.sp.QueryStorageProviderResponse:
     type: object
     properties:
       storageProvider:
         type: object
         properties:
+          id:
+            type: integer
+            format: int64
+            title: >-
+              // id is the identifier of the storage provider, used in virtual
+              group
           operator_address:
             type: string
             description: >-
@@ -30675,6 +31540,12 @@ definitions:
               details:
                 type: string
                 description: details define other optional details.
+          bls_key:
+            type: string
+            format: byte
+            title: >-
+              bls_key defines the bls pub key of the Storage provider for
+              sealing object and completing migration
         title: StorageProvider defines the meta info of storage provider
   greenfield.sp.QueryStorageProvidersResponse:
     type: object
@@ -30684,6 +31555,12 @@ definitions:
         items:
           type: object
           properties:
+            id:
+              type: integer
+              format: int64
+              title: >-
+                // id is the identifier of the storage provider, used in virtual
+                group
             operator_address:
               type: string
               description: >-
@@ -30756,6 +31633,12 @@ definitions:
                 details:
                   type: string
                   description: details define other optional details.
+            bls_key:
+              type: string
+              format: byte
+              title: >-
+                bls_key defines the bls pub key of the Storage provider for
+                sealing object and completing migration
           title: StorageProvider defines the meta info of storage provider
       pagination:
         description: pagination defines the pagination in the response.
@@ -30790,9 +31673,10 @@ definitions:
   greenfield.sp.SpStoragePrice:
     type: object
     properties:
-      sp_address:
-        type: string
-        title: sp address
+      sp_id:
+        type: integer
+        format: int64
+        title: sp id
       update_time_sec:
         type: string
         format: int64
@@ -30820,6 +31704,10 @@ definitions:
   greenfield.sp.StorageProvider:
     type: object
     properties:
+      id:
+        type: integer
+        format: int64
+        title: // id is the identifier of the storage provider, used in virtual group
       operator_address:
         type: string
         description: >-
@@ -30884,6 +31772,12 @@ definitions:
           details:
             type: string
             description: details define other optional details.
+      bls_key:
+        type: string
+        format: byte
+        title: >-
+          bls_key defines the bls pub key of the Storage provider for sealing
+          object and completing migration
     title: StorageProvider defines the meta info of storage provider
   greenfield.common.UInt64Value:
     type: object
@@ -31198,36 +32092,6 @@ definitions:
       - RESOURCE_TYPE_OBJECT
       - RESOURCE_TYPE_GROUP
     default: RESOURCE_TYPE_UNSPECIFIED
-  greenfield.storage.BillingInfo:
-    type: object
-    properties:
-      price_time:
-        type: string
-        format: int64
-        title: >-
-          the time of the payment price, used to calculate the charge rate of
-          the bucket
-      total_charge_size:
-        type: string
-        format: uint64
-        title: >-
-          the total size of the objects in the bucket, used to calculate the
-          charge rate of the bucket
-      secondary_sp_objects_size:
-        type: array
-        items:
-          type: object
-          properties:
-            sp_address:
-              type: string
-              title: address is the address of the secondary sp
-            total_charge_size:
-              type: string
-              format: uint64
-              title: size is the total size of the objects in the secondary sp
-          title: secondary sp objects size statistics
-        title: secondary sp objects size statistics
-    title: BillingInfo is the billing information of the bucket
   greenfield.storage.BucketInfo:
     type: object
     properties:
@@ -31270,13 +32134,18 @@ definitions:
       payment_address:
         type: string
         title: payment_address is the address of the payment account
-      primary_sp_address:
-        type: string
+      primary_sp_id:
+        type: integer
+        format: int64
         description: >-
-          primary_sp_address is the address of the primary sp. Objects belongs
-          to this bucket will never
+          primary_sp_id is the unique id of the primary sp. Objects belongs to
+          this bucket will never
 
           leave this SP, unless you explicitly shift them to another SP.
+      global_virtual_group_family_id:
+        type: integer
+        format: int64
+        title: global_virtual_group_family_id defines the unique id of gvg family
       charged_read_quota:
         type: string
         format: uint64
@@ -31288,42 +32157,13 @@ definitions:
           provided by SP and
 
           the ChargeReadQuota specified here.
-      billing_info:
-        title: billing info of the bucket
-        type: object
-        properties:
-          price_time:
-            type: string
-            format: int64
-            title: >-
-              the time of the payment price, used to calculate the charge rate
-              of the bucket
-          total_charge_size:
-            type: string
-            format: uint64
-            title: >-
-              the total size of the objects in the bucket, used to calculate the
-              charge rate of the bucket
-          secondary_sp_objects_size:
-            type: array
-            items:
-              type: object
-              properties:
-                sp_address:
-                  type: string
-                  title: address is the address of the secondary sp
-                total_charge_size:
-                  type: string
-                  format: uint64
-                  title: size is the total size of the objects in the secondary sp
-              title: secondary sp objects size statistics
-            title: secondary sp objects size statistics
       bucket_status:
         description: bucket_status define the status of the bucket.
         type: string
         enum:
           - BUCKET_STATUS_CREATED
           - BUCKET_STATUS_DISCONTINUED
+          - BUCKET_STATUS_MIGRATING
         default: BUCKET_STATUS_CREATED
   greenfield.storage.BucketMetaData:
     type: object
@@ -31355,6 +32195,7 @@ definitions:
     enum:
       - BUCKET_STATUS_CREATED
       - BUCKET_STATUS_DISCONTINUED
+      - BUCKET_STATUS_MIGRATING
     default: BUCKET_STATUS_CREATED
     description: >-
       BucketStatus represents the status of a bucket. After a user successfully
@@ -31417,6 +32258,12 @@ definitions:
     properties:
       owner:
         type: string
+        title: owner is the object owner
+      creator:
+        type: string
+        title: >-
+          creator is the address of the uploader, it always be same as owner
+          address
       bucket_name:
         type: string
         title: bucket_name is the name of the bucket
@@ -31426,6 +32273,9 @@ definitions:
       id:
         type: string
         title: id is the unique identifier of object
+      local_virtual_group_id:
+        type: integer
+        format: int64
       payload_size:
         type: string
         format: uint64
@@ -31486,11 +32336,6 @@ definitions:
         title: |-
           checksums define the root hash of the pieces which stored in a SP.
           add omit tag to omit the field when converting to NFT metadata
-      secondary_sp_addresses:
-        type: array
-        items:
-          type: string
-        title: secondary_sp_addresses define the addresses of secondary_sps
   greenfield.storage.ObjectMetaData:
     type: object
     properties:
@@ -31618,6 +32463,14 @@ definitions:
         type: string
         format: uint64
         title: The max number for deleting policy in each end block
+      min_quota_update_interval:
+        type: string
+        format: uint64
+        title: The min interval for making quota smaller in seconds
+      max_local_virtual_group_num_per_bucket:
+        type: integer
+        format: int64
+        title: the max number of local virtual group per bucket
     description: Params defines the parameters for the module.
   greenfield.storage.QueryBucketNFTResponse:
     type: object
@@ -31720,13 +32573,18 @@ definitions:
           payment_address:
             type: string
             title: payment_address is the address of the payment account
-          primary_sp_address:
-            type: string
+          primary_sp_id:
+            type: integer
+            format: int64
             description: >-
-              primary_sp_address is the address of the primary sp. Objects
-              belongs to this bucket will never
+              primary_sp_id is the unique id of the primary sp. Objects belongs
+              to this bucket will never
 
               leave this SP, unless you explicitly shift them to another SP.
+          global_virtual_group_family_id:
+            type: integer
+            format: int64
+            title: global_virtual_group_family_id defines the unique id of gvg family
           charged_read_quota:
             type: string
             format: uint64
@@ -31738,44 +32596,13 @@ definitions:
               data provided by SP and
 
               the ChargeReadQuota specified here.
-          billing_info:
-            title: billing info of the bucket
-            type: object
-            properties:
-              price_time:
-                type: string
-                format: int64
-                title: >-
-                  the time of the payment price, used to calculate the charge
-                  rate of the bucket
-              total_charge_size:
-                type: string
-                format: uint64
-                title: >-
-                  the total size of the objects in the bucket, used to calculate
-                  the charge rate of the bucket
-              secondary_sp_objects_size:
-                type: array
-                items:
-                  type: object
-                  properties:
-                    sp_address:
-                      type: string
-                      title: address is the address of the secondary sp
-                    total_charge_size:
-                      type: string
-                      format: uint64
-                      title: >-
-                        size is the total size of the objects in the secondary
-                        sp
-                  title: secondary sp objects size statistics
-                title: secondary sp objects size statistics
           bucket_status:
             description: bucket_status define the status of the bucket.
             type: string
             enum:
               - BUCKET_STATUS_CREATED
               - BUCKET_STATUS_DISCONTINUED
+              - BUCKET_STATUS_MIGRATING
             default: BUCKET_STATUS_CREATED
   greenfield.storage.QueryHeadGroupMemberResponse:
     type: object
@@ -31830,6 +32657,12 @@ definitions:
         properties:
           owner:
             type: string
+            title: owner is the object owner
+          creator:
+            type: string
+            title: >-
+              creator is the address of the uploader, it always be same as owner
+              address
           bucket_name:
             type: string
             title: bucket_name is the name of the bucket
@@ -31839,6 +32672,9 @@ definitions:
           id:
             type: string
             title: id is the unique identifier of object
+          local_virtual_group_id:
+            type: integer
+            format: int64
           payload_size:
             type: string
             format: uint64
@@ -31899,11 +32735,6 @@ definitions:
             title: |-
               checksums define the root hash of the pieces which stored in a SP.
               add omit tag to omit the field when converting to NFT metadata
-          secondary_sp_addresses:
-            type: array
-            items:
-              type: string
-            title: secondary_sp_addresses define the addresses of secondary_sps
   greenfield.storage.QueryListBucketsResponse:
     type: object
     properties:
@@ -31951,13 +32782,20 @@ definitions:
             payment_address:
               type: string
               title: payment_address is the address of the payment account
-            primary_sp_address:
-              type: string
+            primary_sp_id:
+              type: integer
+              format: int64
               description: >-
-                primary_sp_address is the address of the primary sp. Objects
+                primary_sp_id is the unique id of the primary sp. Objects
                 belongs to this bucket will never
 
                 leave this SP, unless you explicitly shift them to another SP.
+            global_virtual_group_family_id:
+              type: integer
+              format: int64
+              title: >-
+                global_virtual_group_family_id defines the unique id of gvg
+                family
             charged_read_quota:
               type: string
               format: uint64
@@ -31969,44 +32807,13 @@ definitions:
                 read data provided by SP and
 
                 the ChargeReadQuota specified here.
-            billing_info:
-              title: billing info of the bucket
-              type: object
-              properties:
-                price_time:
-                  type: string
-                  format: int64
-                  title: >-
-                    the time of the payment price, used to calculate the charge
-                    rate of the bucket
-                total_charge_size:
-                  type: string
-                  format: uint64
-                  title: >-
-                    the total size of the objects in the bucket, used to
-                    calculate the charge rate of the bucket
-                secondary_sp_objects_size:
-                  type: array
-                  items:
-                    type: object
-                    properties:
-                      sp_address:
-                        type: string
-                        title: address is the address of the secondary sp
-                      total_charge_size:
-                        type: string
-                        format: uint64
-                        title: >-
-                          size is the total size of the objects in the secondary
-                          sp
-                    title: secondary sp objects size statistics
-                  title: secondary sp objects size statistics
             bucket_status:
               description: bucket_status define the status of the bucket.
               type: string
               enum:
                 - BUCKET_STATUS_CREATED
                 - BUCKET_STATUS_DISCONTINUED
+                - BUCKET_STATUS_MIGRATING
               default: BUCKET_STATUS_CREATED
       pagination:
         type: object
@@ -32102,6 +32909,12 @@ definitions:
           properties:
             owner:
               type: string
+              title: owner is the object owner
+            creator:
+              type: string
+              title: >-
+                creator is the address of the uploader, it always be same as
+                owner address
             bucket_name:
               type: string
               title: bucket_name is the name of the bucket
@@ -32111,6 +32924,9 @@ definitions:
             id:
               type: string
               title: id is the unique identifier of object
+            local_virtual_group_id:
+              type: integer
+              format: int64
             payload_size:
               type: string
               format: uint64
@@ -32173,11 +32989,6 @@ definitions:
                 SP.
 
                 add omit tag to omit the field when converting to NFT metadata
-            secondary_sp_addresses:
-              type: array
-              items:
-                type: string
-              title: secondary_sp_addresses define the addresses of secondary_sps
       pagination:
         type: object
         properties:
@@ -32325,6 +33136,14 @@ definitions:
             type: string
             format: uint64
             title: The max number for deleting policy in each end block
+          min_quota_update_interval:
+            type: string
+            format: uint64
+            title: The min interval for making quota smaller in seconds
+          max_local_virtual_group_num_per_bucket:
+            type: integer
+            format: int64
+            title: the max number of local virtual group per bucket
     description: >-
       QueryVersionedParamsResponse is response type for the Query/Params RPC
       method with timestamp.
@@ -32421,6 +33240,14 @@ definitions:
             type: string
             format: uint64
             title: The max number for deleting policy in each end block
+          min_quota_update_interval:
+            type: string
+            format: uint64
+            title: The min interval for making quota smaller in seconds
+          max_local_virtual_group_num_per_bucket:
+            type: integer
+            format: int64
+            title: the max number of local virtual group per bucket
     description: QueryParamsResponse is response type for the Query/Params RPC method.
   greenfield.storage.QueryPolicyByIdResponse:
     type: object
@@ -32900,17 +33727,6 @@ definitions:
     description: |-
       RedundancyType represents the redundancy algorithm type for object data,
       which can be either multi-replica or erasure coding.
-  greenfield.storage.SecondarySpObjectsSize:
-    type: object
-    properties:
-      sp_address:
-        type: string
-        title: address is the address of the secondary sp
-      total_charge_size:
-        type: string
-        format: uint64
-        title: size is the total size of the objects in the secondary sp
-    title: secondary sp objects size statistics
   greenfield.storage.SourceType:
     type: string
     enum:
diff --git a/testutil/keeper/payment.go b/testutil/keeper/payment.go
deleted file mode 100644
index a8309a686..000000000
--- a/testutil/keeper/payment.go
+++ /dev/null
@@ -1,101 +0,0 @@
-package keeper
-
-import (
-	"testing"
-
-	tmdb "github.com/cometbft/cometbft-db"
-	"github.com/cometbft/cometbft/libs/log"
-	tmproto "github.com/cometbft/cometbft/proto/tendermint/types"
-	"github.com/cosmos/cosmos-sdk/baseapp"
-	"github.com/cosmos/cosmos-sdk/codec"
-	codectypes "github.com/cosmos/cosmos-sdk/codec/types"
-	"github.com/cosmos/cosmos-sdk/store"
-	storetypes "github.com/cosmos/cosmos-sdk/store/types"
-	sdk "github.com/cosmos/cosmos-sdk/types"
-	authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
-	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
-	"github.com/cosmos/cosmos-sdk/x/authz"
-	authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper"
-	bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
-	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
-	govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
-	paramstypes "github.com/cosmos/cosmos-sdk/x/params/types"
-	"github.com/stretchr/testify/require"
-
-	"github.com/bnb-chain/greenfield/x/payment/keeper"
-	"github.com/bnb-chain/greenfield/x/payment/types"
-	spkeeper "github.com/bnb-chain/greenfield/x/sp/keeper"
-	sptypes "github.com/bnb-chain/greenfield/x/sp/types"
-)
-
-func PaymentKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) {
-	storeKeys := sdk.NewKVStoreKeys(
-		banktypes.StoreKey,
-		authtypes.StoreKey,
-		paramstypes.StoreKey,
-		sptypes.StoreKey,
-		authz.ModuleName,
-	)
-	tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey)
-	storeKey := storetypes.NewKVStoreKey(types.StoreKey)
-	memStoreKey := storetypes.NewMemoryStoreKey(types.MemStoreKey)
-
-	db := tmdb.NewMemDB()
-	stateStore := store.NewCommitMultiStore(db)
-	stateStore.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db)
-	stateStore.MountStoreWithDB(memStoreKey, storetypes.StoreTypeMemory, nil)
-	stateStore.MountStoreWithDB(storeKeys[paramstypes.StoreKey], storetypes.StoreTypeIAVL, db)
-	stateStore.MountStoreWithDB(storeKeys[authtypes.StoreKey], storetypes.StoreTypeIAVL, db)
-	stateStore.MountStoreWithDB(storeKeys[banktypes.StoreKey], storetypes.StoreTypeIAVL, db)
-	stateStore.MountStoreWithDB(tkeys[paramstypes.TStoreKey], storetypes.StoreTypeTransient, nil)
-	require.NoError(t, stateStore.LoadLatestVersion())
-
-	registry := codectypes.NewInterfaceRegistry()
-	cdc := codec.NewProtoCodec(registry)
-
-	accountKeeper := authkeeper.NewAccountKeeper(
-		cdc,
-		storeKeys[authtypes.StoreKey],
-		authtypes.ProtoBaseAccount,
-		spMaccPerms,
-		authtypes.NewModuleAddress(govtypes.ModuleName).String(),
-	)
-
-	bankKeeper := bankkeeper.NewBaseKeeper(
-		cdc,
-		storeKeys[banktypes.StoreKey],
-		accountKeeper,
-		nil,
-		authtypes.NewModuleAddress(govtypes.ModuleName).String(),
-	)
-	authzKeeper := authzkeeper.NewKeeper(
-		storeKeys[authz.ModuleName],
-		cdc,
-		baseapp.NewMsgServiceRouter(),
-		accountKeeper,
-	)
-	spKeeper := spkeeper.NewKeeper(
-		cdc,
-		storeKeys[sptypes.ModuleName],
-		accountKeeper,
-		bankKeeper,
-		authzKeeper,
-		authtypes.NewModuleAddress(govtypes.ModuleName).String(),
-	)
-	k := keeper.NewKeeper(
-		cdc,
-		storeKey,
-		bankKeeper,
-		accountKeeper,
-		spKeeper,
-		authtypes.NewModuleAddress(govtypes.ModuleName).String(),
-	)
-
-	ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, nil, log.NewNopLogger())
-
-	// Initialize params
-	err := k.SetParams(ctx, types.DefaultParams())
-	require.NoError(t, err)
-
-	return k, ctx
-}
diff --git a/testutil/keeper/permission.go b/testutil/keeper/permission.go
deleted file mode 100644
index d1f79a228..000000000
--- a/testutil/keeper/permission.go
+++ /dev/null
@@ -1,105 +0,0 @@
-package keeper
-
-import (
-	"testing"
-
-	tmdb "github.com/cometbft/cometbft-db"
-	"github.com/cometbft/cometbft/libs/log"
-	tmproto "github.com/cometbft/cometbft/proto/tendermint/types"
-	"github.com/cosmos/cosmos-sdk/codec"
-	codectypes "github.com/cosmos/cosmos-sdk/codec/types"
-	"github.com/cosmos/cosmos-sdk/store"
-	storetypes "github.com/cosmos/cosmos-sdk/store/types"
-	sdk "github.com/cosmos/cosmos-sdk/types"
-	authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
-	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
-	"github.com/cosmos/cosmos-sdk/x/authz"
-	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
-	crosschaintypes "github.com/cosmos/cosmos-sdk/x/crosschain/types"
-	distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
-	evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types"
-	"github.com/cosmos/cosmos-sdk/x/feegrant"
-	govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
-	"github.com/cosmos/cosmos-sdk/x/group"
-	minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
-	oracletypes "github.com/cosmos/cosmos-sdk/x/oracle/types"
-	paramstypes "github.com/cosmos/cosmos-sdk/x/params/types"
-	slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types"
-	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
-	upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
-	"github.com/stretchr/testify/require"
-
-	paymentmoduletypes "github.com/bnb-chain/greenfield/x/payment/types"
-	"github.com/bnb-chain/greenfield/x/permission/keeper"
-	"github.com/bnb-chain/greenfield/x/permission/types"
-	sptypes "github.com/bnb-chain/greenfield/x/sp/types"
-)
-
-var (
-	permissionMaccPerms = map[string][]string{
-		authtypes.FeeCollectorName:     {authtypes.Minter, authtypes.Staking},
-		stakingtypes.BondedPoolName:    {authtypes.Burner, authtypes.Staking},
-		stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking},
-		govtypes.ModuleName:            {authtypes.Burner},
-		sptypes.ModuleName:             {authtypes.Staking},
-		types.ModuleName:               {authtypes.Staking},
-	}
-)
-
-func PermissionKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) {
-	storeKeys := sdk.NewKVStoreKeys(authtypes.StoreKey, authz.ModuleName, banktypes.StoreKey, stakingtypes.StoreKey,
-		minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey, govtypes.StoreKey,
-		paramstypes.StoreKey, upgradetypes.StoreKey, feegrant.StoreKey, evidencetypes.StoreKey,
-		group.StoreKey,
-		crosschaintypes.StoreKey,
-		paymentmoduletypes.StoreKey,
-		oracletypes.StoreKey, types.StoreKey)
-
-	memStoreKey := storetypes.NewMemoryStoreKey(types.MemStoreKey)
-
-	tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey)
-
-	storeKey := storetypes.NewKVStoreKey(types.StoreKey)
-
-	db := tmdb.NewMemDB()
-	stateStore := store.NewCommitMultiStore(db)
-	stateStore.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db)
-	stateStore.MountStoreWithDB(memStoreKey, storetypes.StoreTypeMemory, nil)
-
-	stateStore.MountStoreWithDB(storeKeys[paramstypes.StoreKey], storetypes.StoreTypeIAVL, db)
-	stateStore.MountStoreWithDB(storeKeys[authtypes.StoreKey], storetypes.StoreTypeIAVL, db)
-	stateStore.MountStoreWithDB(storeKeys[banktypes.StoreKey], storetypes.StoreTypeIAVL, db)
-
-	stateStore.MountStoreWithDB(tkeys[paramstypes.TStoreKey], storetypes.StoreTypeTransient, nil)
-
-	require.NoError(t, stateStore.LoadLatestVersion())
-
-	registry := codectypes.NewInterfaceRegistry()
-	cdc := codec.NewProtoCodec(registry)
-
-	accountKeeper := authkeeper.NewAccountKeeper(
-		cdc,
-		storeKeys[authtypes.StoreKey],
-		authtypes.ProtoBaseAccount,
-		permissionMaccPerms,
-		authtypes.NewModuleAddress(govtypes.ModuleName).String(),
-	)
-
-	k := keeper.NewKeeper(
-		cdc,
-		storeKey,
-		accountKeeper,
-		authtypes.NewModuleAddress(govtypes.ModuleName).String(),
-	)
-	ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, nil, log.NewNopLogger())
-
-	// Initialize params
-	err := k.SetParams(ctx, types.DefaultParams())
-	require.NoError(t, err)
-
-	err = accountKeeper.SetParams(ctx, authtypes.DefaultParams())
-	require.NoError(t, err)
-
-	return k, ctx
-
-}
diff --git a/testutil/keeper/sp.go b/testutil/keeper/sp.go
deleted file mode 100644
index bb8bb63a5..000000000
--- a/testutil/keeper/sp.go
+++ /dev/null
@@ -1,128 +0,0 @@
-package keeper
-
-import (
-	"testing"
-
-	tmdb "github.com/cometbft/cometbft-db"
-	"github.com/cometbft/cometbft/libs/log"
-	tmproto "github.com/cometbft/cometbft/proto/tendermint/types"
-	"github.com/cosmos/cosmos-sdk/baseapp"
-	"github.com/cosmos/cosmos-sdk/codec"
-	codectypes "github.com/cosmos/cosmos-sdk/codec/types"
-	"github.com/cosmos/cosmos-sdk/store"
-	storetypes "github.com/cosmos/cosmos-sdk/store/types"
-	sdk "github.com/cosmos/cosmos-sdk/types"
-	authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
-	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
-	"github.com/cosmos/cosmos-sdk/x/authz"
-	authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper"
-	bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
-	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
-	crosschaintypes "github.com/cosmos/cosmos-sdk/x/crosschain/types"
-	distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
-	evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types"
-	"github.com/cosmos/cosmos-sdk/x/feegrant"
-	govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
-	"github.com/cosmos/cosmos-sdk/x/group"
-	minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
-	oracletypes "github.com/cosmos/cosmos-sdk/x/oracle/types"
-	paramstypes "github.com/cosmos/cosmos-sdk/x/params/types"
-	slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types"
-	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
-	upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
-	"github.com/stretchr/testify/require"
-
-	"github.com/bnb-chain/greenfield/x/sp/keeper"
-	"github.com/bnb-chain/greenfield/x/sp/types"
-	storagemoduletypes "github.com/bnb-chain/greenfield/x/storage/types"
-)
-
-var (
-	spMaccPerms = map[string][]string{
-		authtypes.FeeCollectorName:     {authtypes.Minter, authtypes.Staking},
-		stakingtypes.BondedPoolName:    {authtypes.Burner, authtypes.Staking},
-		stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking},
-		govtypes.ModuleName:            {authtypes.Burner},
-		types.ModuleName:               {authtypes.Staking},
-	}
-)
-
-func SpKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) {
-	storeKeys := sdk.NewKVStoreKeys(authtypes.StoreKey, authz.ModuleName, banktypes.StoreKey, stakingtypes.StoreKey,
-		minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey, govtypes.StoreKey,
-		paramstypes.StoreKey, upgradetypes.StoreKey, feegrant.StoreKey, evidencetypes.StoreKey,
-		group.StoreKey,
-		storagemoduletypes.StoreKey,
-		crosschaintypes.StoreKey,
-		oracletypes.StoreKey, types.StoreKey)
-
-	tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey)
-
-	storeKey := storetypes.NewKVStoreKey(types.StoreKey)
-
-	memStoreKey := storetypes.NewMemoryStoreKey(types.MemStoreKey)
-
-	db := tmdb.NewMemDB()
-	stateStore := store.NewCommitMultiStore(db)
-	stateStore.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db)
-	stateStore.MountStoreWithDB(memStoreKey, storetypes.StoreTypeMemory, nil)
-	stateStore.MountStoreWithDB(storeKeys[paramstypes.StoreKey], storetypes.StoreTypeIAVL, db)
-	stateStore.MountStoreWithDB(storeKeys[authtypes.StoreKey], storetypes.StoreTypeIAVL, db)
-	stateStore.MountStoreWithDB(storeKeys[banktypes.StoreKey], storetypes.StoreTypeIAVL, db)
-
-	stateStore.MountStoreWithDB(tkeys[paramstypes.TStoreKey], storetypes.StoreTypeTransient, nil)
-
-	require.NoError(t, stateStore.LoadLatestVersion())
-
-	registry := codectypes.NewInterfaceRegistry()
-	cdc := codec.NewProtoCodec(registry)
-
-	accountKeeper := authkeeper.NewAccountKeeper(
-		cdc,
-		storeKeys[authtypes.StoreKey],
-		authtypes.ProtoBaseAccount,
-		spMaccPerms,
-		authtypes.NewModuleAddress(govtypes.ModuleName).String(),
-	)
-
-	authzKeeper := authzkeeper.NewKeeper(
-		storeKeys[authz.ModuleName],
-		cdc,
-		baseapp.NewMsgServiceRouter(),
-		accountKeeper,
-	)
-
-	bankKeeper := bankkeeper.NewBaseKeeper(
-		cdc,
-		storeKeys[banktypes.StoreKey],
-		accountKeeper,
-		nil,
-		authtypes.NewModuleAddress(govtypes.ModuleName).String(),
-	)
-
-	k := keeper.NewKeeper(
-		cdc,
-		storeKey,
-		accountKeeper, bankKeeper, authzKeeper,
-		authtypes.NewModuleAddress(govtypes.ModuleName).String(),
-	)
-
-	ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, nil, log.NewNopLogger())
-
-	// Initialize params
-	err := k.SetParams(ctx, types.DefaultParams())
-	require.NoError(t, err)
-
-	err = accountKeeper.SetParams(ctx, authtypes.DefaultParams())
-	require.NoError(t, err)
-
-	err = bankKeeper.MintCoins(ctx, authtypes.FeeCollectorName, sdk.Coins{sdk.Coin{
-		Denom:  "stake",
-		Amount: sdk.NewInt(1000000000),
-	}})
-	if err != nil {
-		panic("mint coins error")
-	}
-
-	return k, ctx
-}
diff --git a/testutil/keeper/storage.go b/testutil/keeper/storage.go
deleted file mode 100644
index 6543e026b..000000000
--- a/testutil/keeper/storage.go
+++ /dev/null
@@ -1,196 +0,0 @@
-package keeper
-
-import (
-	"testing"
-	"time"
-
-	tmdb "github.com/cometbft/cometbft-db"
-	"github.com/cometbft/cometbft/libs/log"
-	tmproto "github.com/cometbft/cometbft/proto/tendermint/types"
-	"github.com/cosmos/cosmos-sdk/baseapp"
-	"github.com/cosmos/cosmos-sdk/store"
-	storetypes "github.com/cosmos/cosmos-sdk/store/types"
-	sdk "github.com/cosmos/cosmos-sdk/types"
-	authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
-	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
-	"github.com/cosmos/cosmos-sdk/x/authz"
-	authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper"
-	bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
-	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
-	crosschainkeeper "github.com/cosmos/cosmos-sdk/x/crosschain/keeper"
-	crosschaintypes "github.com/cosmos/cosmos-sdk/x/crosschain/types"
-	distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
-	evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types"
-	"github.com/cosmos/cosmos-sdk/x/feegrant"
-	govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
-	"github.com/cosmos/cosmos-sdk/x/group"
-	minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
-	oracletypes "github.com/cosmos/cosmos-sdk/x/oracle/types"
-	paramstypes "github.com/cosmos/cosmos-sdk/x/params/types"
-	slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types"
-	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
-	upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
-	"github.com/stretchr/testify/require"
-
-	"github.com/bnb-chain/greenfield/app"
-	paymentmodulekeeper "github.com/bnb-chain/greenfield/x/payment/keeper"
-	paymentmoduletypes "github.com/bnb-chain/greenfield/x/payment/types"
-	permissionmodulekeeper "github.com/bnb-chain/greenfield/x/permission/keeper"
-	permissionmoduletypes "github.com/bnb-chain/greenfield/x/permission/types"
-	spkeeper "github.com/bnb-chain/greenfield/x/sp/keeper"
-	sptypes "github.com/bnb-chain/greenfield/x/sp/types"
-	"github.com/bnb-chain/greenfield/x/storage/keeper"
-	"github.com/bnb-chain/greenfield/x/storage/types"
-)
-
-var (
-	storageMaccPerms = map[string][]string{
-		authtypes.Minter:               {authtypes.Minter},
-		authtypes.FeeCollectorName:     {authtypes.Minter, authtypes.Staking},
-		stakingtypes.BondedPoolName:    {authtypes.Burner, authtypes.Staking},
-		stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking},
-		govtypes.ModuleName:            {authtypes.Burner},
-		sptypes.ModuleName:             {authtypes.Staking},
-		types.ModuleName:               {authtypes.Staking},
-		paymentmoduletypes.ModuleName:  {},
-	}
-)
-
-type StorageDepKeepers struct {
-	PaymentKeeper *paymentmodulekeeper.Keeper
-	SpKeeper      *spkeeper.Keeper
-	BankKeeper    *bankkeeper.BaseKeeper
-	AccountKeeper *authkeeper.AccountKeeper
-}
-
-func StorageKeeper(t testing.TB) (*keeper.Keeper, StorageDepKeepers, sdk.Context) {
-	storeKeys := sdk.NewKVStoreKeys(authtypes.StoreKey, authz.ModuleName, banktypes.StoreKey, stakingtypes.StoreKey,
-		minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey, govtypes.StoreKey,
-		paramstypes.StoreKey, upgradetypes.StoreKey, feegrant.StoreKey, evidencetypes.StoreKey,
-		group.StoreKey,
-		crosschaintypes.StoreKey,
-		sptypes.StoreKey,
-		paymentmoduletypes.StoreKey,
-		permissionmoduletypes.StoreKey,
-		oracletypes.StoreKey, types.StoreKey)
-
-	memStoreKey := storetypes.NewMemoryStoreKey(types.MemStoreKey)
-
-	storeKey := storetypes.NewKVStoreKey(types.StoreKey)
-	tStorekey := storetypes.NewTransientStoreKey(types.TStoreKey)
-
-	db := tmdb.NewMemDB()
-	stateStore := store.NewCommitMultiStore(db)
-	stateStore.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db)
-	stateStore.MountStoreWithDB(memStoreKey, storetypes.StoreTypeMemory, nil)
-	stateStore.MountStoreWithDB(storeKeys[paramstypes.StoreKey], storetypes.StoreTypeIAVL, db)
-	stateStore.MountStoreWithDB(storeKeys[authtypes.StoreKey], storetypes.StoreTypeIAVL, db)
-	stateStore.MountStoreWithDB(storeKeys[banktypes.StoreKey], storetypes.StoreTypeIAVL, db)
-	stateStore.MountStoreWithDB(storeKeys[paymentmoduletypes.StoreKey], storetypes.StoreTypeIAVL, db)
-	stateStore.MountStoreWithDB(storeKeys[sptypes.StoreKey], storetypes.StoreTypeIAVL, db)
-	stateStore.MountStoreWithDB(storeKeys[crosschaintypes.StoreKey], storetypes.StoreTypeIAVL, db)
-	require.NoError(t, stateStore.LoadLatestVersion())
-
-	cdcConfig := app.MakeEncodingConfig()
-	cdc := cdcConfig.Marshaler
-
-	accountKeeper := authkeeper.NewAccountKeeper(
-		cdc,
-		storeKeys[authtypes.StoreKey],
-		authtypes.ProtoBaseAccount,
-		storageMaccPerms,
-		authtypes.NewModuleAddress(govtypes.ModuleName).String(),
-	)
-
-	authzKeeper := authzkeeper.NewKeeper(
-		storeKeys[authz.ModuleName],
-		cdc,
-		baseapp.NewMsgServiceRouter(),
-		accountKeeper,
-	)
-
-	bankKeeper := bankkeeper.NewBaseKeeper(
-		cdc,
-		storeKeys[banktypes.StoreKey],
-		accountKeeper,
-		nil,
-		authtypes.NewModuleAddress(govtypes.ModuleName).String(),
-	)
-
-	spKeeper := spkeeper.NewKeeper(
-		cdc,
-		storeKeys[sptypes.ModuleName],
-		accountKeeper,
-		bankKeeper,
-		authzKeeper,
-		authtypes.NewModuleAddress(govtypes.ModuleName).String(),
-	)
-
-	paymentKeeper := paymentmodulekeeper.NewKeeper(
-		cdc,
-		storeKeys[paymentmoduletypes.StoreKey],
-
-		bankKeeper,
-		accountKeeper,
-		spKeeper,
-
-		authtypes.NewModuleAddress(govtypes.ModuleName).String(),
-	)
-	crossChainKeeper := crosschainkeeper.NewKeeper(
-		cdc,
-		storeKeys[crosschaintypes.StoreKey],
-		authtypes.NewModuleAddress(govtypes.ModuleName).String(),
-	)
-
-	permissionKeeper := permissionmodulekeeper.NewKeeper(
-		cdc,
-		storeKeys[permissionmoduletypes.ModuleName],
-		accountKeeper,
-		authtypes.NewModuleAddress(govtypes.ModuleName).String(),
-	)
-
-	k := keeper.NewKeeper(
-		cdc,
-		storeKey,
-		tStorekey,
-		accountKeeper,
-		spKeeper,
-		paymentKeeper,
-		permissionKeeper,
-		crossChainKeeper,
-		authtypes.NewModuleAddress(govtypes.ModuleName).String(),
-	)
-
-	ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, nil, log.NewNopLogger())
-	ctx = ctx.WithBlockTime(time.Now())
-
-	// Initialize params
-	err := k.SetParams(ctx, types.DefaultParams())
-	require.NoError(t, err)
-	err = accountKeeper.SetParams(ctx, authtypes.DefaultParams())
-	require.NoError(t, err)
-	err = spKeeper.SetParams(ctx, sptypes.DefaultParams())
-	require.NoError(t, err)
-	err = paymentKeeper.SetParams(ctx, paymentmoduletypes.DefaultParams())
-	require.NoError(t, err)
-
-	// Initialize module accounts
-	paymentModulePool := accountKeeper.GetModuleAccount(ctx, paymentmoduletypes.ModuleName)
-	accountKeeper.SetModuleAccount(ctx, paymentModulePool)
-
-	amount := sdk.NewIntFromUint64(1e19)
-	err = bankKeeper.MintCoins(ctx, authtypes.Minter, sdk.Coins{sdk.Coin{
-		Denom:  "BNB",
-		Amount: amount,
-	}})
-	if err != nil {
-		panic("mint coins error")
-	}
-
-	return k, StorageDepKeepers{
-		SpKeeper:      spKeeper,
-		PaymentKeeper: paymentKeeper,
-		BankKeeper:    &bankKeeper,
-		AccountKeeper: &accountKeeper,
-	}, ctx
-}
diff --git a/testutil/sample/sample.go b/testutil/sample/sample.go
index 7087cbafc..96aaf945c 100644
--- a/testutil/sample/sample.go
+++ b/testutil/sample/sample.go
@@ -2,9 +2,12 @@ package sample
 
 import (
 	"crypto/rand"
+	"encoding/hex"
 
+	"github.com/cometbft/cometbft/crypto/tmhash"
 	"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
 	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/prysmaticlabs/prysm/crypto/bls"
 )
 
 // AccAddress returns a sample account address
@@ -30,3 +33,25 @@ func RandStr(length int) []byte {
 	_, _ = rand.Read(randBytes)
 	return randBytes
 }
+
+func RandBlsPubKey() []byte {
+	blsPrivKey, _ := bls.RandKey()
+	return blsPrivKey.PublicKey().Marshal()
+}
+
+func RandBlsPubKeyHex() string {
+	blsPrivKey, _ := bls.RandKey()
+	return hex.EncodeToString(blsPrivKey.PublicKey().Marshal())
+}
+
+func RandBlsPubKeyAndBlsProofBz() ([]byte, []byte) {
+	blsPriv, _ := bls.RandKey()
+	blsPubKeyBz := blsPriv.PublicKey().Marshal()
+	blsProofBz := blsPriv.Sign(tmhash.Sum(blsPubKeyBz)).Marshal()
+	return blsPubKeyBz, blsProofBz
+}
+
+func RandBlsPubKeyAndBlsProof() (string, string) {
+	blsPubKey, proof := RandBlsPubKeyAndBlsProofBz()
+	return hex.EncodeToString(blsPubKey), hex.EncodeToString(proof)
+}
diff --git a/types/common/approval.pb.go b/types/common/approval.pb.go
new file mode 100644
index 000000000..8c4f80fc1
--- /dev/null
+++ b/types/common/approval.pb.go
@@ -0,0 +1,403 @@
+// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// source: greenfield/common/approval.proto
+
+package common
+
+import (
+	fmt "fmt"
+	_ "github.com/cosmos/cosmos-proto"
+	_ "github.com/cosmos/cosmos-sdk/codec/types"
+	_ "github.com/cosmos/gogoproto/gogoproto"
+	proto "github.com/cosmos/gogoproto/proto"
+	io "io"
+	math "math"
+	math_bits "math/bits"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
+
+// Approval is the signature information returned by the Primary Storage Provider (SP) to the user
+// after allowing them to create a bucket or object, which is then used for verification on the chain
+// to ensure agreement between the Primary SP and the user.
+type Approval struct {
+	// expired_height is the block height at which the signature expires.
+	ExpiredHeight uint64 `protobuf:"varint,1,opt,name=expired_height,json=expiredHeight,proto3" json:"expired_height,omitempty"`
+	// global_virtual_group_family_id is the family id that stored.
+	GlobalVirtualGroupFamilyId uint32 `protobuf:"varint,2,opt,name=global_virtual_group_family_id,json=globalVirtualGroupFamilyId,proto3" json:"global_virtual_group_family_id,omitempty"`
+	// The signature needs to conform to the EIP 712 specification.
+	Sig []byte `protobuf:"bytes,3,opt,name=sig,proto3" json:"sig,omitempty"`
+}
+
+func (m *Approval) Reset()         { *m = Approval{} }
+func (m *Approval) String() string { return proto.CompactTextString(m) }
+func (*Approval) ProtoMessage()    {}
+func (*Approval) Descriptor() ([]byte, []int) {
+	return fileDescriptor_fe7c4fc8fb6d4918, []int{0}
+}
+func (m *Approval) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *Approval) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_Approval.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *Approval) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Approval.Merge(m, src)
+}
+func (m *Approval) XXX_Size() int {
+	return m.Size()
+}
+func (m *Approval) XXX_DiscardUnknown() {
+	xxx_messageInfo_Approval.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Approval proto.InternalMessageInfo
+
+func (m *Approval) GetExpiredHeight() uint64 {
+	if m != nil {
+		return m.ExpiredHeight
+	}
+	return 0
+}
+
+func (m *Approval) GetGlobalVirtualGroupFamilyId() uint32 {
+	if m != nil {
+		return m.GlobalVirtualGroupFamilyId
+	}
+	return 0
+}
+
+func (m *Approval) GetSig() []byte {
+	if m != nil {
+		return m.Sig
+	}
+	return nil
+}
+
+func init() {
+	proto.RegisterType((*Approval)(nil), "greenfield.common.Approval")
+}
+
+func init() { proto.RegisterFile("greenfield/common/approval.proto", fileDescriptor_fe7c4fc8fb6d4918) }
+
+var fileDescriptor_fe7c4fc8fb6d4918 = []byte{
+	// 278 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0xd0, 0x31, 0x4b, 0xf4, 0x30,
+	0x18, 0x07, 0xf0, 0xe6, 0xbd, 0x17, 0x91, 0xe2, 0x89, 0x16, 0x87, 0x5e, 0x87, 0x50, 0x04, 0xa1,
+	0x83, 0x5e, 0x06, 0x3f, 0x81, 0x37, 0x9c, 0xba, 0x76, 0x70, 0x70, 0x09, 0x49, 0x9b, 0xa6, 0x81,
+	0xb4, 0x4f, 0x48, 0xdb, 0xc3, 0x7e, 0x02, 0x57, 0x3f, 0x96, 0xe3, 0x8d, 0x8e, 0xd2, 0x7e, 0x11,
+	0x69, 0x53, 0xd0, 0xed, 0x79, 0xfe, 0xf9, 0x41, 0x78, 0xfe, 0x7e, 0x2c, 0xad, 0x10, 0x75, 0xa1,
+	0x84, 0xce, 0x49, 0x06, 0x55, 0x05, 0x35, 0x61, 0xc6, 0x58, 0x38, 0x30, 0xbd, 0x35, 0x16, 0x5a,
+	0x08, 0x2e, 0x7f, 0xc5, 0xd6, 0x89, 0x68, 0x93, 0x41, 0x53, 0x41, 0x43, 0x67, 0x40, 0xdc, 0xe2,
+	0x74, 0x74, 0x25, 0x41, 0x82, 0xcb, 0xa7, 0x69, 0x49, 0x37, 0x12, 0x40, 0x6a, 0x41, 0xe6, 0x8d,
+	0x77, 0x05, 0x61, 0x75, 0xef, 0x9e, 0xae, 0xdf, 0x91, 0x7f, 0xfa, 0xb0, 0xfc, 0x18, 0xdc, 0xf8,
+	0xe7, 0xe2, 0xcd, 0x28, 0x2b, 0x72, 0x5a, 0x0a, 0x25, 0xcb, 0x36, 0x44, 0x31, 0x4a, 0xfe, 0xa7,
+	0xeb, 0x25, 0x7d, 0x9a, 0xc3, 0x60, 0xe7, 0x63, 0xa9, 0x81, 0x33, 0x4d, 0x0f, 0xca, 0xb6, 0x1d,
+	0xd3, 0x54, 0x5a, 0xe8, 0x0c, 0x2d, 0x58, 0xa5, 0x74, 0x4f, 0x55, 0x1e, 0xfe, 0x8b, 0x51, 0xb2,
+	0x4e, 0x23, 0xa7, 0x5e, 0x1c, 0x7a, 0x9c, 0xcc, 0x7e, 0x26, 0xcf, 0x79, 0x70, 0xe1, 0xaf, 0x1a,
+	0x25, 0xc3, 0x55, 0x8c, 0x92, 0xb3, 0x74, 0x1a, 0x77, 0xfb, 0xcf, 0x01, 0xa3, 0xe3, 0x80, 0xd1,
+	0xf7, 0x80, 0xd1, 0xc7, 0x88, 0xbd, 0xe3, 0x88, 0xbd, 0xaf, 0x11, 0x7b, 0xaf, 0xb7, 0x52, 0xb5,
+	0x65, 0xc7, 0xa7, 0xf3, 0x09, 0xaf, 0xf9, 0x5d, 0x56, 0x32, 0x55, 0x93, 0x3f, 0xcd, 0xb5, 0xbd,
+	0x11, 0xcd, 0xd2, 0x1f, 0x3f, 0x99, 0x0f, 0xbb, 0xff, 0x09, 0x00, 0x00, 0xff, 0xff, 0xd4, 0x66,
+	0x9c, 0xf8, 0x5b, 0x01, 0x00, 0x00,
+}
+
+func (m *Approval) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *Approval) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *Approval) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.Sig) > 0 {
+		i -= len(m.Sig)
+		copy(dAtA[i:], m.Sig)
+		i = encodeVarintApproval(dAtA, i, uint64(len(m.Sig)))
+		i--
+		dAtA[i] = 0x1a
+	}
+	if m.GlobalVirtualGroupFamilyId != 0 {
+		i = encodeVarintApproval(dAtA, i, uint64(m.GlobalVirtualGroupFamilyId))
+		i--
+		dAtA[i] = 0x10
+	}
+	if m.ExpiredHeight != 0 {
+		i = encodeVarintApproval(dAtA, i, uint64(m.ExpiredHeight))
+		i--
+		dAtA[i] = 0x8
+	}
+	return len(dAtA) - i, nil
+}
+
+func encodeVarintApproval(dAtA []byte, offset int, v uint64) int {
+	offset -= sovApproval(v)
+	base := offset
+	for v >= 1<<7 {
+		dAtA[offset] = uint8(v&0x7f | 0x80)
+		v >>= 7
+		offset++
+	}
+	dAtA[offset] = uint8(v)
+	return base
+}
+func (m *Approval) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.ExpiredHeight != 0 {
+		n += 1 + sovApproval(uint64(m.ExpiredHeight))
+	}
+	if m.GlobalVirtualGroupFamilyId != 0 {
+		n += 1 + sovApproval(uint64(m.GlobalVirtualGroupFamilyId))
+	}
+	l = len(m.Sig)
+	if l > 0 {
+		n += 1 + l + sovApproval(uint64(l))
+	}
+	return n
+}
+
+func sovApproval(x uint64) (n int) {
+	return (math_bits.Len64(x|1) + 6) / 7
+}
+func sozApproval(x uint64) (n int) {
+	return sovApproval(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *Approval) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowApproval
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: Approval: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: Approval: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ExpiredHeight", wireType)
+			}
+			m.ExpiredHeight = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowApproval
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.ExpiredHeight |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupFamilyId", wireType)
+			}
+			m.GlobalVirtualGroupFamilyId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowApproval
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.GlobalVirtualGroupFamilyId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Sig", wireType)
+			}
+			var byteLen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowApproval
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				byteLen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if byteLen < 0 {
+				return ErrInvalidLengthApproval
+			}
+			postIndex := iNdEx + byteLen
+			if postIndex < 0 {
+				return ErrInvalidLengthApproval
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Sig = append(m.Sig[:0], dAtA[iNdEx:postIndex]...)
+			if m.Sig == nil {
+				m.Sig = []byte{}
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipApproval(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthApproval
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func skipApproval(dAtA []byte) (n int, err error) {
+	l := len(dAtA)
+	iNdEx := 0
+	depth := 0
+	for iNdEx < l {
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return 0, ErrIntOverflowApproval
+			}
+			if iNdEx >= l {
+				return 0, io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		wireType := int(wire & 0x7)
+		switch wireType {
+		case 0:
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowApproval
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				iNdEx++
+				if dAtA[iNdEx-1] < 0x80 {
+					break
+				}
+			}
+		case 1:
+			iNdEx += 8
+		case 2:
+			var length int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowApproval
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				length |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if length < 0 {
+				return 0, ErrInvalidLengthApproval
+			}
+			iNdEx += length
+		case 3:
+			depth++
+		case 4:
+			if depth == 0 {
+				return 0, ErrUnexpectedEndOfGroupApproval
+			}
+			depth--
+		case 5:
+			iNdEx += 4
+		default:
+			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+		}
+		if iNdEx < 0 {
+			return 0, ErrInvalidLengthApproval
+		}
+		if depth == 0 {
+			return iNdEx, nil
+		}
+	}
+	return 0, io.ErrUnexpectedEOF
+}
+
+var (
+	ErrInvalidLengthApproval        = fmt.Errorf("proto: negative length found during unmarshaling")
+	ErrIntOverflowApproval          = fmt.Errorf("proto: integer overflow")
+	ErrUnexpectedEndOfGroupApproval = fmt.Errorf("proto: unexpected end of group")
+)
diff --git a/types/errors/errors.go b/types/errors/errors.go
index f2b1dc2be..decd19f73 100644
--- a/types/errors/errors.go
+++ b/types/errors/errors.go
@@ -4,7 +4,7 @@ import (
 	"cosmossdk.io/errors"
 )
 
-const RootCodespace = "greenfiled"
+const RootCodespace = "greenfield"
 
 var (
 	ErrInvalidBucketName     = errors.Register(RootCodespace, 1000, "Invalid bucket name")
@@ -20,6 +20,8 @@ var (
 	ErrInvalidVisibilityType = errors.Register(RootCodespace, 1010, "Invalid public type")
 	ErrInvalidActionType     = errors.Register(RootCodespace, 1011, "Invalid action type")
 	ErrInvalidPrincipalType  = errors.Register(RootCodespace, 1012, "Invalid principal type")
+	ErrInvalidBlsSignature   = errors.Register(RootCodespace, 1013, "bls signature is invalid")
+	ErrInvalidMessage        = errors.Register(RootCodespace, 1014, "Invalid message")
 
 	ErrGRNTypeMismatch = errors.Register(RootCodespace, 2000, "Greenfield resource type mismatch")
 )
diff --git a/x/storage/types/signature.go b/types/verifier.go
similarity index 65%
rename from x/storage/types/signature.go
rename to types/verifier.go
index f74c040a0..0fdc1939a 100644
--- a/x/storage/types/signature.go
+++ b/types/verifier.go
@@ -8,6 +8,9 @@ import (
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 	ethcrypto "github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/crypto/secp256k1"
+	"github.com/prysmaticlabs/prysm/crypto/bls"
+
+	gnfderrors "github.com/bnb-chain/greenfield/types/errors"
 )
 
 func VerifySignature(sigAccAddress sdk.AccAddress, sigHash []byte, sig []byte) error {
@@ -46,3 +49,25 @@ func VerifySignature(sigAccAddress sdk.AccAddress, sigHash []byte, sig []byte) e
 
 	return nil
 }
+
+func VerifyBlsSignature(blsPubKey bls.PublicKey, sigHash [32]byte, blsSig []byte) error {
+	sig, err := bls.SignatureFromBytes(blsSig)
+	if err != nil {
+		return errors.Wrapf(gnfderrors.ErrInvalidBlsSignature, "BLS signature conversion failed: %v", err)
+	}
+	if !sig.Verify(blsPubKey, sigHash[:]) {
+		return errors.Wrapf(gnfderrors.ErrInvalidBlsSignature, "signature verification failed")
+	}
+	return nil
+}
+
+func VerifyBlsAggSignature(blsPubKeys []bls.PublicKey, sigHash [32]byte, blsAggSig []byte) error {
+	aggSig, err := bls.SignatureFromBytes(blsAggSig)
+	if err != nil {
+		return errors.Wrapf(gnfderrors.ErrInvalidBlsSignature, "BLS signature conversion failed: %v", err)
+	}
+	if !aggSig.FastAggregateVerify(blsPubKeys[:], sigHash) {
+		return errors.Wrapf(gnfderrors.ErrInvalidBlsSignature, "aggregated signature verification failed")
+	}
+	return nil
+}
diff --git a/x/bridge/keeper/cross_app.go b/x/bridge/keeper/cross_app.go
index 3063eed5b..de0a4dc08 100644
--- a/x/bridge/keeper/cross_app.go
+++ b/x/bridge/keeper/cross_app.go
@@ -96,6 +96,7 @@ func (app *TransferOutApp) ExecuteAckPackage(ctx sdk.Context, appCtx *sdk.CrossC
 		},
 		RefundReason: types.RefundReason(refundPackage.RefundReason),
 		Sequence:     appCtx.Sequence,
+		DestChainId:  uint32(appCtx.SrcChainId),
 	})
 	if err != nil {
 		app.bridgeKeeper.Logger(ctx).Error("emit event error", "err", err.Error())
@@ -146,6 +147,7 @@ func (app *TransferOutApp) ExecuteFailAckPackage(ctx sdk.Context, appCtx *sdk.Cr
 		},
 		RefundReason: types.REFUND_REASON_FAIL_ACK,
 		Sequence:     appCtx.Sequence,
+		DestChainId:  uint32(appCtx.SrcChainId),
 	})
 	if err != nil {
 		app.bridgeKeeper.Logger(ctx).Error("emit event error", "err", err.Error())
@@ -233,6 +235,7 @@ func (app *TransferInApp) ExecuteSynPackage(ctx sdk.Context, appCtx *sdk.CrossCh
 		ReceiverAddress: transferInPackage.ReceiverAddress.String(),
 		RefundAddress:   transferInPackage.RefundAddress.String(),
 		Sequence:        appCtx.Sequence,
+		SrcChainId:      uint32(appCtx.SrcChainId),
 	})
 	if err != nil {
 		app.bridgeKeeper.Logger(ctx).Error("emit event error", "err", err.Error())
diff --git a/x/bridge/keeper/cross_app_test.go b/x/bridge/keeper/cross_app_test.go
index fb283d092..beee21139 100644
--- a/x/bridge/keeper/cross_app_test.go
+++ b/x/bridge/keeper/cross_app_test.go
@@ -5,7 +5,6 @@ import (
 	"math/big"
 	"testing"
 
-	"github.com/cosmos/cosmos-sdk/bsc/rlp"
 	"github.com/cosmos/cosmos-sdk/crypto/hd"
 	"github.com/cosmos/cosmos-sdk/testutil"
 	sdk "github.com/cosmos/cosmos-sdk/types"
@@ -72,7 +71,7 @@ func (s *TestSuite) TestTransferOutAck() {
 		RefundReason: 1,
 	}
 
-	packageBytes, err := rlp.EncodeToBytes(&refundPackage)
+	packageBytes, err := refundPackage.Serialize()
 	s.Require().Nil(err, "encode refund package error")
 
 	transferOutApp := keeper.NewTransferOutApp(*s.bridgeKeeper)
@@ -94,12 +93,12 @@ func (s *TestSuite) TestTransferOutFailAck() {
 		RefundAddress: addr1,
 	}
 
-	packageBytes, err := rlp.EncodeToBytes(&synPackage)
+	packageBytes, err := synPackage.Serialize()
 	s.Require().Nil(err, "encode refund package error")
 
 	transferOutApp := keeper.NewTransferOutApp(*s.bridgeKeeper)
 
-	s.crossChainKeeper.EXPECT().CreateRawIBCPackageWithFee(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(uint64(0), nil).AnyTimes()
+	s.crossChainKeeper.EXPECT().CreateRawIBCPackageWithFee(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(uint64(0), nil).AnyTimes()
 	s.stakingKeeper.EXPECT().BondDenom(gomock.Any()).Return("BNB").AnyTimes()
 	s.bankKeeper.EXPECT().SendCoinsFromModuleToAccount(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
 
@@ -172,12 +171,12 @@ func (s *TestSuite) TestTransferInSyn() {
 		RefundAddress:   sdk.AccAddress{1},
 	}
 
-	packageBytes, err := rlp.EncodeToBytes(&transferInSynPackage)
+	packageBytes, err := transferInSynPackage.Serialize()
 	s.Require().Nil(err, "encode refund package error")
 
 	transferInApp := keeper.NewTransferInApp(*s.bridgeKeeper)
 
-	s.crossChainKeeper.EXPECT().CreateRawIBCPackageWithFee(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(uint64(0), nil).AnyTimes()
+	s.crossChainKeeper.EXPECT().CreateRawIBCPackageWithFee(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(uint64(0), nil).AnyTimes()
 	s.stakingKeeper.EXPECT().BondDenom(gomock.Any()).Return("BNB").AnyTimes()
 	s.bankKeeper.EXPECT().SendCoinsFromModuleToAccount(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
 
diff --git a/x/bridge/keeper/keeper.go b/x/bridge/keeper/keeper.go
index c0436bf72..e2573b474 100644
--- a/x/bridge/keeper/keeper.go
+++ b/x/bridge/keeper/keeper.go
@@ -5,7 +5,6 @@ import (
 
 	"cosmossdk.io/errors"
 	"github.com/cometbft/cometbft/libs/log"
-	"github.com/cosmos/cosmos-sdk/bsc/rlp"
 	"github.com/cosmos/cosmos-sdk/codec"
 	storetypes "github.com/cosmos/cosmos-sdk/store/types"
 	sdk "github.com/cosmos/cosmos-sdk/types"
@@ -59,7 +58,7 @@ func (k Keeper) GetRefundTransferInPayload(transferInClaim *types.TransferInSynP
 		RefundReason:  refundReason,
 	}
 
-	encodedBytes, err := rlp.EncodeToBytes(refundPackage)
+	encodedBytes, err := refundPackage.Serialize()
 	if err != nil {
 		return nil, errors.Wrapf(types.ErrInvalidPackage, "encode refund package error")
 	}
diff --git a/x/bridge/keeper/msg_server_test.go b/x/bridge/keeper/msg_server_test.go
index d75bd04ed..2794ec141 100644
--- a/x/bridge/keeper/msg_server_test.go
+++ b/x/bridge/keeper/msg_server_test.go
@@ -84,7 +84,8 @@ func (s *TestSuite) TestCrossTransferOut() {
 
 	s.stakingKeeper.EXPECT().BondDenom(gomock.Any()).Return("BNB").AnyTimes()
 	s.bankKeeper.EXPECT().SendCoinsFromAccountToModule(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
-	s.crossChainKeeper.EXPECT().CreateRawIBCPackageWithFee(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(uint64(0), nil).AnyTimes()
+	s.crossChainKeeper.EXPECT().GetDestBscChainID().Return(sdk.ChainID(714)).AnyTimes()
+	s.crossChainKeeper.EXPECT().CreateRawIBCPackageWithFee(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(uint64(0), nil).AnyTimes()
 
 	msgTransferOut := types.NewMsgTransferOut(addr1.String(), addr2.String(), &sdk.Coin{
 		Denom:  "BNB",
diff --git a/x/bridge/keeper/msg_server_transfer_out.go b/x/bridge/keeper/msg_server_transfer_out.go
index c6296878a..be0080a02 100644
--- a/x/bridge/keeper/msg_server_transfer_out.go
+++ b/x/bridge/keeper/msg_server_transfer_out.go
@@ -4,7 +4,6 @@ import (
 	"context"
 
 	"cosmossdk.io/errors"
-	"github.com/cosmos/cosmos-sdk/bsc/rlp"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	crosschaintypes "github.com/cosmos/cosmos-sdk/x/crosschain/types"
 
@@ -42,12 +41,12 @@ func (k msgServer) TransferOut(goCtx context.Context, msg *types.MsgTransferOut)
 		Amount:        msg.Amount.Amount.BigInt(),
 	}
 
-	encodedPackage, err := rlp.EncodeToBytes(transferPackage)
+	encodedPackage, err := transferPackage.Serialize()
 	if err != nil {
 		return nil, errors.Wrapf(types.ErrInvalidPackage, "encode transfer out package error")
 	}
 
-	sendSeq, err := k.crossChainKeeper.CreateRawIBCPackageWithFee(ctx, types.TransferOutChannelID, sdk.SynCrossChainPackageType,
+	sendSeq, err := k.crossChainKeeper.CreateRawIBCPackageWithFee(ctx, k.crossChainKeeper.GetDestBscChainID(), types.TransferOutChannelID, sdk.SynCrossChainPackageType,
 		encodedPackage, relayerFeeAmount.BigInt(), ackRelayerFeeAmount.BigInt())
 	if err != nil {
 		return nil, err
@@ -55,11 +54,12 @@ func (k msgServer) TransferOut(goCtx context.Context, msg *types.MsgTransferOut)
 
 	// emit event
 	transferOutEvent := types.EventCrossTransferOut{
-		From:       fromAddress.String(),
-		To:         toAddress.String(),
-		Amount:     msg.Amount,
-		RelayerFee: &relayerFee,
-		Sequence:   sendSeq,
+		From:        fromAddress.String(),
+		To:          toAddress.String(),
+		Amount:      msg.Amount,
+		RelayerFee:  &relayerFee,
+		Sequence:    sendSeq,
+		DestChainId: uint32(k.crossChainKeeper.GetDestBscChainID()),
 	}
 	err = ctx.EventManager().EmitTypedEvent(&transferOutEvent)
 	if err != nil {
diff --git a/x/bridge/keeper/params.go b/x/bridge/keeper/params.go
index f354be3ae..31f9cf086 100644
--- a/x/bridge/keeper/params.go
+++ b/x/bridge/keeper/params.go
@@ -36,5 +36,5 @@ func (k Keeper) SetParams(ctx sdk.Context, params types.Params) error {
 func (k Keeper) GetTransferOutRelayerFee(ctx sdk.Context) (sdkmath.Int, sdkmath.Int) {
 	params := k.GetParams(ctx)
 
-	return params.TransferOutRelayerFee, params.TransferOutAckRelayerFee
+	return params.BscTransferOutRelayerFee, params.BscTransferOutAckRelayerFee
 }
diff --git a/x/bridge/types/event.pb.go b/x/bridge/types/event.pb.go
index a2d33a7bb..24a292e6f 100644
--- a/x/bridge/types/event.pb.go
+++ b/x/bridge/types/event.pb.go
@@ -64,6 +64,8 @@ type EventCrossTransferOut struct {
 	RelayerFee *types.Coin `protobuf:"bytes,4,opt,name=relayer_fee,json=relayerFee,proto3" json:"relayer_fee,omitempty"`
 	// Sequence of the corresponding cross chain package
 	Sequence uint64 `protobuf:"varint,5,opt,name=sequence,proto3" json:"sequence,omitempty"`
+	// Destination chain id of the cross chain transfer tx
+	DestChainId uint32 `protobuf:"varint,6,opt,name=dest_chain_id,json=destChainId,proto3" json:"dest_chain_id,omitempty"`
 }
 
 func (m *EventCrossTransferOut) Reset()         { *m = EventCrossTransferOut{} }
@@ -134,6 +136,13 @@ func (m *EventCrossTransferOut) GetSequence() uint64 {
 	return 0
 }
 
+func (m *EventCrossTransferOut) GetDestChainId() uint32 {
+	if m != nil {
+		return m.DestChainId
+	}
+	return 0
+}
+
 // EventCrossTransferOutRefund is emitted when a cross chain transfer out tx failed
 type EventCrossTransferOutRefund struct {
 	// Refund address of the failed cross chain transfer tx
@@ -144,6 +153,8 @@ type EventCrossTransferOutRefund struct {
 	RefundReason RefundReason `protobuf:"varint,3,opt,name=refund_reason,json=refundReason,proto3,enum=greenfield.bridge.RefundReason" json:"refund_reason,omitempty"`
 	// Sequence of the corresponding cross chain package
 	Sequence uint64 `protobuf:"varint,4,opt,name=sequence,proto3" json:"sequence,omitempty"`
+	// Destination chain id of the cross chain transfer tx
+	DestChainId uint32 `protobuf:"varint,5,opt,name=dest_chain_id,json=destChainId,proto3" json:"dest_chain_id,omitempty"`
 }
 
 func (m *EventCrossTransferOutRefund) Reset()         { *m = EventCrossTransferOutRefund{} }
@@ -207,6 +218,13 @@ func (m *EventCrossTransferOutRefund) GetSequence() uint64 {
 	return 0
 }
 
+func (m *EventCrossTransferOutRefund) GetDestChainId() uint32 {
+	if m != nil {
+		return m.DestChainId
+	}
+	return 0
+}
+
 // EventCrossTransferIn is emitted when a cross chain transfer in tx happened
 type EventCrossTransferIn struct {
 	// Amount of the cross chain transfer tx
@@ -217,6 +235,8 @@ type EventCrossTransferIn struct {
 	RefundAddress string `protobuf:"bytes,3,opt,name=refund_address,json=refundAddress,proto3" json:"refund_address,omitempty"`
 	// Sequence of the corresponding cross chain package
 	Sequence uint64 `protobuf:"varint,4,opt,name=sequence,proto3" json:"sequence,omitempty"`
+	// Source chain id of the cross chain transfer tx
+	SrcChainId uint32 `protobuf:"varint,5,opt,name=src_chain_id,json=srcChainId,proto3" json:"src_chain_id,omitempty"`
 }
 
 func (m *EventCrossTransferIn) Reset()         { *m = EventCrossTransferIn{} }
@@ -280,6 +300,13 @@ func (m *EventCrossTransferIn) GetSequence() uint64 {
 	return 0
 }
 
+func (m *EventCrossTransferIn) GetSrcChainId() uint32 {
+	if m != nil {
+		return m.SrcChainId
+	}
+	return 0
+}
+
 func init() {
 	proto.RegisterEnum("greenfield.bridge.RefundReason", RefundReason_name, RefundReason_value)
 	proto.RegisterType((*EventCrossTransferOut)(nil), "greenfield.bridge.EventCrossTransferOut")
@@ -290,38 +317,41 @@ func init() {
 func init() { proto.RegisterFile("greenfield/bridge/event.proto", fileDescriptor_7d0c64a57c5987e0) }
 
 var fileDescriptor_7d0c64a57c5987e0 = []byte{
-	// 492 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0x4f, 0x6e, 0xd3, 0x40,
-	0x14, 0xc6, 0x33, 0x69, 0xa8, 0x60, 0x5a, 0x42, 0x18, 0xb5, 0x28, 0x35, 0xc2, 0x44, 0x91, 0x40,
-	0x01, 0x89, 0xb1, 0x52, 0x76, 0xec, 0x5c, 0xd7, 0x16, 0x56, 0x2b, 0x47, 0x72, 0x1b, 0x21, 0xb1,
-	0xb1, 0xfc, 0xe7, 0xd9, 0xb5, 0xd4, 0xcc, 0x94, 0x19, 0x3b, 0xa2, 0x37, 0x60, 0xc9, 0x1d, 0x38,
-	0x04, 0x37, 0x40, 0x2c, 0xbb, 0x84, 0x1d, 0x4a, 0x2e, 0x82, 0x62, 0x3b, 0xc5, 0x21, 0x15, 0x74,
-	0xf7, 0xe6, 0x7b, 0x6f, 0x46, 0xdf, 0xef, 0x1b, 0x3d, 0xfc, 0x24, 0x11, 0x00, 0x2c, 0x4e, 0xe1,
-	0x3c, 0xd2, 0x02, 0x91, 0x46, 0x09, 0x68, 0x30, 0x05, 0x96, 0xd1, 0x0b, 0xc1, 0x33, 0x4e, 0x1e,
-	0xfe, 0x69, 0xd3, 0xb2, 0xad, 0xa8, 0x21, 0x97, 0x13, 0x2e, 0xb5, 0xc0, 0x97, 0xa0, 0x4d, 0x87,
-	0x01, 0x64, 0xfe, 0x50, 0x0b, 0x79, 0xca, 0xca, 0x2b, 0xca, 0x4e, 0xc2, 0x13, 0x5e, 0x94, 0xda,
-	0xa2, 0x2a, 0xd5, 0xfe, 0x37, 0x84, 0x77, 0xcd, 0xc5, 0xc3, 0x86, 0xe0, 0x52, 0x9e, 0x0a, 0x9f,
-	0xc9, 0x18, 0xc4, 0x28, 0xcf, 0x08, 0xc1, 0xad, 0x58, 0xf0, 0x49, 0x17, 0xf5, 0xd0, 0xe0, 0x9e,
-	0x5b, 0xd4, 0xa4, 0x8d, 0x9b, 0x19, 0xef, 0x36, 0x0b, 0xa5, 0x99, 0x71, 0x32, 0xc4, 0x9b, 0xfe,
-	0x84, 0xe7, 0x2c, 0xeb, 0x6e, 0xf4, 0xd0, 0x60, 0x6b, 0x7f, 0x8f, 0x96, 0x26, 0xe8, 0xc2, 0x04,
-	0xad, 0x4c, 0x50, 0x83, 0xa7, 0xcc, 0xad, 0x06, 0xc9, 0x1b, 0xbc, 0x25, 0xe0, 0xdc, 0xbf, 0x04,
-	0xe1, 0xc5, 0x00, 0xdd, 0xd6, 0xff, 0xee, 0xe1, 0x6a, 0xda, 0x02, 0x20, 0x0a, 0xbe, 0x2b, 0xe1,
-	0x43, 0x0e, 0x2c, 0x84, 0xee, 0x9d, 0x1e, 0x1a, 0xb4, 0xdc, 0xeb, 0x73, 0xff, 0x27, 0xc2, 0x8f,
-	0x6f, 0x04, 0x71, 0x21, 0xce, 0x59, 0x44, 0x9e, 0xe1, 0xb6, 0x28, 0x2a, 0xcf, 0x8f, 0x22, 0x01,
-	0x52, 0x56, 0x60, 0xf7, 0x4b, 0x55, 0x2f, 0xc5, 0x1a, 0x51, 0xf3, 0xb6, 0x44, 0x87, 0xb8, 0x7a,
-	0xc3, 0x13, 0xe0, 0x4b, 0xce, 0x8a, 0x2c, 0xda, 0xfb, 0x4f, 0xe9, 0xda, 0x1f, 0xd1, 0xd2, 0x8b,
-	0x5b, 0x8c, 0xb9, 0xdb, 0xa2, 0x76, 0x5a, 0x61, 0x6b, 0xfd, 0xc5, 0xf6, 0x15, 0xe1, 0x9d, 0x75,
-	0x36, 0x9b, 0xd5, 0xdc, 0xa2, 0xdb, 0xba, 0x7d, 0x81, 0x3b, 0x02, 0x42, 0x48, 0xa7, 0x20, 0xae,
-	0x93, 0x28, 0x3f, 0xf4, 0xc1, 0x52, 0x5f, 0x66, 0xb1, 0x1e, 0xd9, 0xc6, 0x4d, 0x91, 0xfd, 0xc3,
-	0xf9, 0x4b, 0x89, 0xb7, 0xeb, 0xcc, 0x64, 0x0f, 0xef, 0xba, 0xa6, 0x35, 0x76, 0x0e, 0x3d, 0xd7,
-	0xd4, 0x4f, 0x46, 0x8e, 0x37, 0x76, 0x8e, 0x9c, 0xd1, 0x3b, 0xa7, 0xd3, 0x20, 0xcf, 0x71, 0x7f,
-	0xb5, 0x65, 0x3b, 0x27, 0x63, 0xcb, 0xb2, 0x0d, 0xdb, 0x74, 0x4e, 0xbd, 0x03, 0xfd, 0x58, 0x77,
-	0x0c, 0xb3, 0x83, 0x88, 0x82, 0x1f, 0xad, 0xce, 0x59, 0xba, 0x7d, 0xec, 0xe9, 0xc6, 0x51, 0xa7,
-	0xa9, 0xb4, 0x3e, 0x7d, 0x51, 0x1b, 0x07, 0x6f, 0xbf, 0xcf, 0x54, 0x74, 0x35, 0x53, 0xd1, 0xaf,
-	0x99, 0x8a, 0x3e, 0xcf, 0xd5, 0xc6, 0xd5, 0x5c, 0x6d, 0xfc, 0x98, 0xab, 0x8d, 0xf7, 0x34, 0x49,
-	0xb3, 0xb3, 0x3c, 0xa0, 0x21, 0x9f, 0x68, 0x01, 0x0b, 0x5e, 0x85, 0x67, 0x7e, 0xca, 0xb4, 0xda,
-	0xaa, 0x7d, 0x5c, 0x2e, 0x5b, 0x76, 0x79, 0x01, 0x32, 0xd8, 0x2c, 0x96, 0xe4, 0xf5, 0xef, 0x00,
-	0x00, 0x00, 0xff, 0xff, 0xc8, 0x14, 0xb3, 0x30, 0x8e, 0x03, 0x00, 0x00,
+	// 529 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0xcf, 0x6e, 0xd3, 0x40,
+	0x10, 0xc6, 0xb3, 0x69, 0x1a, 0xc1, 0xe4, 0x0f, 0x61, 0xd5, 0x22, 0x37, 0x08, 0x13, 0x45, 0x02,
+	0x05, 0x24, 0x6c, 0xa5, 0xdc, 0xb8, 0xb9, 0x6e, 0x22, 0xac, 0x56, 0x8e, 0xe4, 0x36, 0x42, 0xe2,
+	0x62, 0xf9, 0xcf, 0x24, 0xb5, 0xd4, 0xec, 0x96, 0x5d, 0x27, 0xa2, 0x6f, 0xc0, 0x91, 0x77, 0xe0,
+	0x65, 0x38, 0xf6, 0x08, 0x37, 0x94, 0xdc, 0x78, 0x07, 0x24, 0x14, 0xdb, 0x29, 0x0e, 0xa9, 0xda,
+	0xde, 0x66, 0xbf, 0x99, 0x5d, 0x7d, 0xbf, 0xcf, 0x1e, 0x78, 0x36, 0x16, 0x88, 0x6c, 0x14, 0xe1,
+	0x79, 0xa8, 0xfb, 0x22, 0x0a, 0xc7, 0xa8, 0xe3, 0x0c, 0x59, 0xac, 0x5d, 0x08, 0x1e, 0x73, 0xfa,
+	0xf8, 0x5f, 0x5b, 0x4b, 0xdb, 0x4d, 0x35, 0xe0, 0x72, 0xc2, 0xa5, 0xee, 0x7b, 0x12, 0xf5, 0x59,
+	0xd7, 0xc7, 0xd8, 0xeb, 0xea, 0x01, 0x8f, 0x58, 0x7a, 0xa5, 0xb9, 0x33, 0xe6, 0x63, 0x9e, 0x94,
+	0xfa, 0xb2, 0x4a, 0xd5, 0xf6, 0x6f, 0x02, 0xbb, 0xbd, 0xe5, 0xc3, 0xa6, 0xe0, 0x52, 0x9e, 0x0a,
+	0x8f, 0xc9, 0x11, 0x8a, 0xc1, 0x34, 0xa6, 0x14, 0x4a, 0x23, 0xc1, 0x27, 0x0a, 0x69, 0x91, 0xce,
+	0x43, 0x27, 0xa9, 0x69, 0x1d, 0x8a, 0x31, 0x57, 0x8a, 0x89, 0x52, 0x8c, 0x39, 0xed, 0x42, 0xd9,
+	0x9b, 0xf0, 0x29, 0x8b, 0x95, 0xad, 0x16, 0xe9, 0x54, 0xf6, 0xf7, 0xb4, 0xd4, 0x84, 0xb6, 0x34,
+	0xa1, 0x65, 0x26, 0x34, 0x93, 0x47, 0xcc, 0xc9, 0x06, 0xe9, 0x3b, 0xa8, 0x08, 0x3c, 0xf7, 0x2e,
+	0x51, 0xb8, 0x23, 0x44, 0xa5, 0x74, 0xd7, 0x3d, 0xc8, 0xa6, 0xfb, 0x88, 0xb4, 0x09, 0x0f, 0x24,
+	0x7e, 0x9a, 0x22, 0x0b, 0x50, 0xd9, 0x6e, 0x91, 0x4e, 0xc9, 0xb9, 0x3e, 0xd3, 0x36, 0xd4, 0x42,
+	0x94, 0xb1, 0x1b, 0x9c, 0x79, 0x11, 0x73, 0xa3, 0x50, 0x29, 0xb7, 0x48, 0xa7, 0xe6, 0x54, 0x96,
+	0xa2, 0xb9, 0xd4, 0xac, 0xb0, 0xfd, 0x87, 0xc0, 0xd3, 0x1b, 0x61, 0x1d, 0x1c, 0x4d, 0x59, 0x48,
+	0x5f, 0x40, 0x5d, 0x24, 0x95, 0xeb, 0x85, 0xa1, 0x40, 0x29, 0x33, 0xf8, 0x5a, 0xaa, 0x1a, 0xa9,
+	0x98, 0xa3, 0x2e, 0xde, 0x97, 0xfa, 0x10, 0xb2, 0x37, 0x5c, 0x81, 0x9e, 0xe4, 0x2c, 0xc9, 0xab,
+	0xbe, 0xff, 0x5c, 0xdb, 0xf8, 0x8e, 0x5a, 0xea, 0xc5, 0x49, 0xc6, 0x9c, 0xaa, 0xc8, 0x9d, 0xd6,
+	0xf8, 0x4b, 0x77, 0xf1, 0x6f, 0x6f, 0xf2, 0xff, 0x24, 0xb0, 0xb3, 0xc9, 0x6f, 0xb1, 0x1c, 0x11,
+	0xb9, 0x2f, 0xd1, 0x2b, 0x68, 0x08, 0x0c, 0x30, 0x9a, 0xa1, 0xb8, 0x4e, 0x2b, 0xfd, 0x31, 0x1e,
+	0xad, 0xf4, 0x55, 0x5e, 0x9b, 0xb1, 0x6e, 0xdd, 0x14, 0xeb, 0x6d, 0x74, 0x2d, 0xa8, 0x4a, 0x11,
+	0xfc, 0x0f, 0x07, 0x52, 0x04, 0x19, 0xdb, 0x6b, 0x09, 0xd5, 0x7c, 0x72, 0x74, 0x0f, 0x76, 0x9d,
+	0x5e, 0x7f, 0x68, 0x1f, 0xba, 0x4e, 0xcf, 0x38, 0x19, 0xd8, 0xee, 0xd0, 0x3e, 0xb2, 0x07, 0x1f,
+	0xec, 0x46, 0x81, 0xbe, 0x84, 0xf6, 0x7a, 0xcb, 0xb2, 0x4f, 0x86, 0xfd, 0xbe, 0x65, 0x5a, 0x3d,
+	0xfb, 0xd4, 0x3d, 0x30, 0x8e, 0x0d, 0xdb, 0xec, 0x35, 0x08, 0x6d, 0xc2, 0x93, 0xf5, 0xb9, 0xbe,
+	0x61, 0x1d, 0xbb, 0x86, 0x79, 0xd4, 0x28, 0x36, 0x4b, 0x5f, 0xbe, 0xa9, 0x85, 0x83, 0xf7, 0xdf,
+	0xe7, 0x2a, 0xb9, 0x9a, 0xab, 0xe4, 0xd7, 0x5c, 0x25, 0x5f, 0x17, 0x6a, 0xe1, 0x6a, 0xa1, 0x16,
+	0x7e, 0x2c, 0xd4, 0xc2, 0x47, 0x6d, 0x1c, 0xc5, 0x67, 0x53, 0x5f, 0x0b, 0xf8, 0x44, 0xf7, 0x99,
+	0xff, 0x26, 0x71, 0xae, 0xe7, 0x96, 0xfa, 0xf3, 0x6a, 0xad, 0xe3, 0xcb, 0x0b, 0x94, 0x7e, 0x39,
+	0x59, 0xc7, 0xb7, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x74, 0x71, 0x97, 0xf6, 0xf8, 0x03, 0x00,
+	0x00,
 }
 
 func (m *EventCrossTransferOut) Marshal() (dAtA []byte, err error) {
@@ -344,6 +374,11 @@ func (m *EventCrossTransferOut) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	_ = i
 	var l int
 	_ = l
+	if m.DestChainId != 0 {
+		i = encodeVarintEvent(dAtA, i, uint64(m.DestChainId))
+		i--
+		dAtA[i] = 0x30
+	}
 	if m.Sequence != 0 {
 		i = encodeVarintEvent(dAtA, i, uint64(m.Sequence))
 		i--
@@ -410,6 +445,11 @@ func (m *EventCrossTransferOutRefund) MarshalToSizedBuffer(dAtA []byte) (int, er
 	_ = i
 	var l int
 	_ = l
+	if m.DestChainId != 0 {
+		i = encodeVarintEvent(dAtA, i, uint64(m.DestChainId))
+		i--
+		dAtA[i] = 0x28
+	}
 	if m.Sequence != 0 {
 		i = encodeVarintEvent(dAtA, i, uint64(m.Sequence))
 		i--
@@ -462,6 +502,11 @@ func (m *EventCrossTransferIn) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	_ = i
 	var l int
 	_ = l
+	if m.SrcChainId != 0 {
+		i = encodeVarintEvent(dAtA, i, uint64(m.SrcChainId))
+		i--
+		dAtA[i] = 0x28
+	}
 	if m.Sequence != 0 {
 		i = encodeVarintEvent(dAtA, i, uint64(m.Sequence))
 		i--
@@ -532,6 +577,9 @@ func (m *EventCrossTransferOut) Size() (n int) {
 	if m.Sequence != 0 {
 		n += 1 + sovEvent(uint64(m.Sequence))
 	}
+	if m.DestChainId != 0 {
+		n += 1 + sovEvent(uint64(m.DestChainId))
+	}
 	return n
 }
 
@@ -555,6 +603,9 @@ func (m *EventCrossTransferOutRefund) Size() (n int) {
 	if m.Sequence != 0 {
 		n += 1 + sovEvent(uint64(m.Sequence))
 	}
+	if m.DestChainId != 0 {
+		n += 1 + sovEvent(uint64(m.DestChainId))
+	}
 	return n
 }
 
@@ -579,6 +630,9 @@ func (m *EventCrossTransferIn) Size() (n int) {
 	if m.Sequence != 0 {
 		n += 1 + sovEvent(uint64(m.Sequence))
 	}
+	if m.SrcChainId != 0 {
+		n += 1 + sovEvent(uint64(m.SrcChainId))
+	}
 	return n
 }
 
@@ -772,6 +826,25 @@ func (m *EventCrossTransferOut) Unmarshal(dAtA []byte) error {
 					break
 				}
 			}
+		case 6:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field DestChainId", wireType)
+			}
+			m.DestChainId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvent
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.DestChainId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
 		default:
 			iNdEx = preIndex
 			skippy, err := skipEvent(dAtA[iNdEx:])
@@ -928,6 +1001,25 @@ func (m *EventCrossTransferOutRefund) Unmarshal(dAtA []byte) error {
 					break
 				}
 			}
+		case 5:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field DestChainId", wireType)
+			}
+			m.DestChainId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvent
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.DestChainId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
 		default:
 			iNdEx = preIndex
 			skippy, err := skipEvent(dAtA[iNdEx:])
@@ -1097,6 +1189,25 @@ func (m *EventCrossTransferIn) Unmarshal(dAtA []byte) error {
 					break
 				}
 			}
+		case 5:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field SrcChainId", wireType)
+			}
+			m.SrcChainId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvent
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.SrcChainId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
 		default:
 			iNdEx = preIndex
 			skippy, err := skipEvent(dAtA[iNdEx:])
diff --git a/x/bridge/types/expected_keeper_mocks.go b/x/bridge/types/expected_keeper_mocks.go
index 1232ea25a..ffeea1854 100644
--- a/x/bridge/types/expected_keeper_mocks.go
+++ b/x/bridge/types/expected_keeper_mocks.go
@@ -9,173 +9,225 @@ import (
 	reflect "reflect"
 
 	types "github.com/cosmos/cosmos-sdk/types"
+	types0 "github.com/cosmos/cosmos-sdk/x/auth/types"
 	gomock "github.com/golang/mock/gomock"
 )
 
-// MockBankKeeper is a mock of BankKeeper interface
+// MockAccountKeeper is a mock of AccountKeeper interface.
+type MockAccountKeeper struct {
+	ctrl     *gomock.Controller
+	recorder *MockAccountKeeperMockRecorder
+}
+
+// MockAccountKeeperMockRecorder is the mock recorder for MockAccountKeeper.
+type MockAccountKeeperMockRecorder struct {
+	mock *MockAccountKeeper
+}
+
+// NewMockAccountKeeper creates a new mock instance.
+func NewMockAccountKeeper(ctrl *gomock.Controller) *MockAccountKeeper {
+	mock := &MockAccountKeeper{ctrl: ctrl}
+	mock.recorder = &MockAccountKeeperMockRecorder{mock}
+	return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockAccountKeeper) EXPECT() *MockAccountKeeperMockRecorder {
+	return m.recorder
+}
+
+// GetAccount mocks base method.
+func (m *MockAccountKeeper) GetAccount(ctx types.Context, addr types.AccAddress) types0.AccountI {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetAccount", ctx, addr)
+	ret0, _ := ret[0].(types0.AccountI)
+	return ret0
+}
+
+// GetAccount indicates an expected call of GetAccount.
+func (mr *MockAccountKeeperMockRecorder) GetAccount(ctx, addr interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetAccount), ctx, addr)
+}
+
+// MockBankKeeper is a mock of BankKeeper interface.
 type MockBankKeeper struct {
 	ctrl     *gomock.Controller
 	recorder *MockBankKeeperMockRecorder
 }
 
-// MockBankKeeperMockRecorder is the mock recorder for MockBankKeeper
+// MockBankKeeperMockRecorder is the mock recorder for MockBankKeeper.
 type MockBankKeeperMockRecorder struct {
 	mock *MockBankKeeper
 }
 
-// NewMockBankKeeper creates a new mock instance
+// NewMockBankKeeper creates a new mock instance.
 func NewMockBankKeeper(ctrl *gomock.Controller) *MockBankKeeper {
 	mock := &MockBankKeeper{ctrl: ctrl}
 	mock.recorder = &MockBankKeeperMockRecorder{mock}
 	return mock
 }
 
-// EXPECT returns an object that allows the caller to indicate expected use
+// EXPECT returns an object that allows the caller to indicate expected use.
 func (m *MockBankKeeper) EXPECT() *MockBankKeeperMockRecorder {
 	return m.recorder
 }
 
-// SendCoinsFromAccountToModule mocks base method
-func (m *MockBankKeeper) SendCoinsFromAccountToModule(arg0 types.Context, arg1 types.AccAddress, arg2 string, arg3 types.Coins) error {
+// SendCoinsFromAccountToModule mocks base method.
+func (m *MockBankKeeper) SendCoinsFromAccountToModule(ctx types.Context, senderAddr types.AccAddress, recipientModule string, amt types.Coins) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "SendCoinsFromAccountToModule", arg0, arg1, arg2, arg3)
+	ret := m.ctrl.Call(m, "SendCoinsFromAccountToModule", ctx, senderAddr, recipientModule, amt)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
-// SendCoinsFromAccountToModule indicates an expected call of SendCoinsFromAccountToModule
-func (mr *MockBankKeeperMockRecorder) SendCoinsFromAccountToModule(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
+// SendCoinsFromAccountToModule indicates an expected call of SendCoinsFromAccountToModule.
+func (mr *MockBankKeeperMockRecorder) SendCoinsFromAccountToModule(ctx, senderAddr, recipientModule, amt interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromAccountToModule", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromAccountToModule), arg0, arg1, arg2, arg3)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromAccountToModule", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromAccountToModule), ctx, senderAddr, recipientModule, amt)
 }
 
-// SendCoinsFromModuleToAccount mocks base method
-func (m *MockBankKeeper) SendCoinsFromModuleToAccount(arg0 types.Context, arg1 string, arg2 types.AccAddress, arg3 types.Coins) error {
+// SendCoinsFromModuleToAccount mocks base method.
+func (m *MockBankKeeper) SendCoinsFromModuleToAccount(ctx types.Context, senderModule string, recipientAddr types.AccAddress, amt types.Coins) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "SendCoinsFromModuleToAccount", arg0, arg1, arg2, arg3)
+	ret := m.ctrl.Call(m, "SendCoinsFromModuleToAccount", ctx, senderModule, recipientAddr, amt)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
-// SendCoinsFromModuleToAccount indicates an expected call of SendCoinsFromModuleToAccount
-func (mr *MockBankKeeperMockRecorder) SendCoinsFromModuleToAccount(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
+// SendCoinsFromModuleToAccount indicates an expected call of SendCoinsFromModuleToAccount.
+func (mr *MockBankKeeperMockRecorder) SendCoinsFromModuleToAccount(ctx, senderModule, recipientAddr, amt interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromModuleToAccount", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromModuleToAccount), arg0, arg1, arg2, arg3)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromModuleToAccount", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromModuleToAccount), ctx, senderModule, recipientAddr, amt)
 }
 
-// SendCoinsFromModuleToModule mocks base method
-func (m *MockBankKeeper) SendCoinsFromModuleToModule(arg0 types.Context, arg1, arg2 string, arg3 types.Coins) error {
+// SendCoinsFromModuleToModule mocks base method.
+func (m *MockBankKeeper) SendCoinsFromModuleToModule(ctx types.Context, senderModule, recipientModule string, amt types.Coins) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "SendCoinsFromModuleToModule", arg0, arg1, arg2, arg3)
+	ret := m.ctrl.Call(m, "SendCoinsFromModuleToModule", ctx, senderModule, recipientModule, amt)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
-// SendCoinsFromModuleToModule indicates an expected call of SendCoinsFromModuleToModule
-func (mr *MockBankKeeperMockRecorder) SendCoinsFromModuleToModule(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
+// SendCoinsFromModuleToModule indicates an expected call of SendCoinsFromModuleToModule.
+func (mr *MockBankKeeperMockRecorder) SendCoinsFromModuleToModule(ctx, senderModule, recipientModule, amt interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromModuleToModule", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromModuleToModule), arg0, arg1, arg2, arg3)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromModuleToModule", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromModuleToModule), ctx, senderModule, recipientModule, amt)
 }
 
-// SpendableCoins mocks base method
-func (m *MockBankKeeper) SpendableCoins(arg0 types.Context, arg1 types.AccAddress) types.Coins {
+// SpendableCoins mocks base method.
+func (m *MockBankKeeper) SpendableCoins(ctx types.Context, addr types.AccAddress) types.Coins {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "SpendableCoins", arg0, arg1)
+	ret := m.ctrl.Call(m, "SpendableCoins", ctx, addr)
 	ret0, _ := ret[0].(types.Coins)
 	return ret0
 }
 
-// SpendableCoins indicates an expected call of SpendableCoins
-func (mr *MockBankKeeperMockRecorder) SpendableCoins(arg0, arg1 interface{}) *gomock.Call {
+// SpendableCoins indicates an expected call of SpendableCoins.
+func (mr *MockBankKeeperMockRecorder) SpendableCoins(ctx, addr interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpendableCoins", reflect.TypeOf((*MockBankKeeper)(nil).SpendableCoins), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpendableCoins", reflect.TypeOf((*MockBankKeeper)(nil).SpendableCoins), ctx, addr)
 }
 
-// MockStakingKeeper is a mock of StakingKeeper interface
+// MockStakingKeeper is a mock of StakingKeeper interface.
 type MockStakingKeeper struct {
 	ctrl     *gomock.Controller
 	recorder *MockStakingKeeperMockRecorder
 }
 
-// MockStakingKeeperMockRecorder is the mock recorder for MockStakingKeeper
+// MockStakingKeeperMockRecorder is the mock recorder for MockStakingKeeper.
 type MockStakingKeeperMockRecorder struct {
 	mock *MockStakingKeeper
 }
 
-// NewMockStakingKeeper creates a new mock instance
+// NewMockStakingKeeper creates a new mock instance.
 func NewMockStakingKeeper(ctrl *gomock.Controller) *MockStakingKeeper {
 	mock := &MockStakingKeeper{ctrl: ctrl}
 	mock.recorder = &MockStakingKeeperMockRecorder{mock}
 	return mock
 }
 
-// EXPECT returns an object that allows the caller to indicate expected use
+// EXPECT returns an object that allows the caller to indicate expected use.
 func (m *MockStakingKeeper) EXPECT() *MockStakingKeeperMockRecorder {
 	return m.recorder
 }
 
-// BondDenom mocks base method
-func (m *MockStakingKeeper) BondDenom(arg0 types.Context) string {
+// BondDenom mocks base method.
+func (m *MockStakingKeeper) BondDenom(ctx types.Context) string {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "BondDenom", arg0)
+	ret := m.ctrl.Call(m, "BondDenom", ctx)
 	ret0, _ := ret[0].(string)
 	return ret0
 }
 
-// BondDenom indicates an expected call of BondDenom
-func (mr *MockStakingKeeperMockRecorder) BondDenom(arg0 interface{}) *gomock.Call {
+// BondDenom indicates an expected call of BondDenom.
+func (mr *MockStakingKeeperMockRecorder) BondDenom(ctx interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BondDenom", reflect.TypeOf((*MockStakingKeeper)(nil).BondDenom), arg0)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BondDenom", reflect.TypeOf((*MockStakingKeeper)(nil).BondDenom), ctx)
 }
 
-// MockCrossChainKeeper is a mock of CrossChainKeeper interface
+// MockCrossChainKeeper is a mock of CrossChainKeeper interface.
 type MockCrossChainKeeper struct {
 	ctrl     *gomock.Controller
 	recorder *MockCrossChainKeeperMockRecorder
 }
 
-// MockCrossChainKeeperMockRecorder is the mock recorder for MockCrossChainKeeper
+// MockCrossChainKeeperMockRecorder is the mock recorder for MockCrossChainKeeper.
 type MockCrossChainKeeperMockRecorder struct {
 	mock *MockCrossChainKeeper
 }
 
-// NewMockCrossChainKeeper creates a new mock instance
+// NewMockCrossChainKeeper creates a new mock instance.
 func NewMockCrossChainKeeper(ctrl *gomock.Controller) *MockCrossChainKeeper {
 	mock := &MockCrossChainKeeper{ctrl: ctrl}
 	mock.recorder = &MockCrossChainKeeperMockRecorder{mock}
 	return mock
 }
 
-// EXPECT returns an object that allows the caller to indicate expected use
+// EXPECT returns an object that allows the caller to indicate expected use.
 func (m *MockCrossChainKeeper) EXPECT() *MockCrossChainKeeperMockRecorder {
 	return m.recorder
 }
 
-// CreateRawIBCPackageWithFee mocks base method
-func (m *MockCrossChainKeeper) CreateRawIBCPackageWithFee(arg0 types.Context, arg1 types.ChannelID, arg2 types.CrossChainPackageType, arg3 []byte, arg4, arg5 *big.Int) (uint64, error) {
+// CreateRawIBCPackageWithFee mocks base method.
+func (m *MockCrossChainKeeper) CreateRawIBCPackageWithFee(ctx types.Context, chainID types.ChainID, channelID types.ChannelID, packageType types.CrossChainPackageType, packageLoad []byte, relayerFee, ackRelayerFee *big.Int) (uint64, error) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "CreateRawIBCPackageWithFee", arg0, arg1, arg2, arg3, arg4, arg5)
+	ret := m.ctrl.Call(m, "CreateRawIBCPackageWithFee", ctx, chainID, channelID, packageType, packageLoad, relayerFee, ackRelayerFee)
 	ret0, _ := ret[0].(uint64)
 	ret1, _ := ret[1].(error)
 	return ret0, ret1
 }
 
-// CreateRawIBCPackageWithFee indicates an expected call of CreateRawIBCPackageWithFee
-func (mr *MockCrossChainKeeperMockRecorder) CreateRawIBCPackageWithFee(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call {
+// CreateRawIBCPackageWithFee indicates an expected call of CreateRawIBCPackageWithFee.
+func (mr *MockCrossChainKeeperMockRecorder) CreateRawIBCPackageWithFee(ctx, chainID, channelID, packageType, packageLoad, relayerFee, ackRelayerFee interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateRawIBCPackageWithFee", reflect.TypeOf((*MockCrossChainKeeper)(nil).CreateRawIBCPackageWithFee), ctx, chainID, channelID, packageType, packageLoad, relayerFee, ackRelayerFee)
+}
+
+// GetDestBscChainID mocks base method.
+func (m *MockCrossChainKeeper) GetDestBscChainID() types.ChainID {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetDestBscChainID")
+	ret0, _ := ret[0].(types.ChainID)
+	return ret0
+}
+
+// GetDestBscChainID indicates an expected call of GetDestBscChainID.
+func (mr *MockCrossChainKeeperMockRecorder) GetDestBscChainID() *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateRawIBCPackageWithFee", reflect.TypeOf((*MockCrossChainKeeper)(nil).CreateRawIBCPackageWithFee), arg0, arg1, arg2, arg3, arg4, arg5)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDestBscChainID", reflect.TypeOf((*MockCrossChainKeeper)(nil).GetDestBscChainID))
 }
 
-// RegisterChannel mocks base method
-func (m *MockCrossChainKeeper) RegisterChannel(arg0 string, arg1 types.ChannelID, arg2 types.CrossChainApplication) error {
+// RegisterChannel mocks base method.
+func (m *MockCrossChainKeeper) RegisterChannel(name string, id types.ChannelID, app types.CrossChainApplication) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "RegisterChannel", arg0, arg1, arg2)
+	ret := m.ctrl.Call(m, "RegisterChannel", name, id, app)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
-// RegisterChannel indicates an expected call of RegisterChannel
-func (mr *MockCrossChainKeeperMockRecorder) RegisterChannel(arg0, arg1, arg2 interface{}) *gomock.Call {
+// RegisterChannel indicates an expected call of RegisterChannel.
+func (mr *MockCrossChainKeeperMockRecorder) RegisterChannel(name, id, app interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterChannel", reflect.TypeOf((*MockCrossChainKeeper)(nil).RegisterChannel), arg0, arg1, arg2)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterChannel", reflect.TypeOf((*MockCrossChainKeeper)(nil).RegisterChannel), name, id, app)
 }
diff --git a/x/bridge/types/expected_keepers.go b/x/bridge/types/expected_keepers.go
index 170218b8a..c9203536b 100644
--- a/x/bridge/types/expected_keepers.go
+++ b/x/bridge/types/expected_keepers.go
@@ -27,7 +27,8 @@ type StakingKeeper interface {
 }
 
 type CrossChainKeeper interface {
-	CreateRawIBCPackageWithFee(ctx sdk.Context, channelID sdk.ChannelID, packageType sdk.CrossChainPackageType,
+	GetDestBscChainID() sdk.ChainID
+	CreateRawIBCPackageWithFee(ctx sdk.Context, chainID sdk.ChainID, channelID sdk.ChannelID, packageType sdk.CrossChainPackageType,
 		packageLoad []byte, relayerFee *big.Int, ackRelayerFee *big.Int,
 	) (uint64, error)
 
diff --git a/x/bridge/types/genesis_test.go b/x/bridge/types/genesis_test.go
index 2a4821b2d..df6fb8809 100644
--- a/x/bridge/types/genesis_test.go
+++ b/x/bridge/types/genesis_test.go
@@ -24,8 +24,8 @@ func TestGenesisState_Validate(t *testing.T) {
 			desc: "valid genesis state",
 			genState: &types.GenesisState{
 				Params: types.Params{
-					TransferOutRelayerFee:    sdkmath.NewInt(1),
-					TransferOutAckRelayerFee: sdkmath.NewInt(0),
+					BscTransferOutRelayerFee:    sdkmath.NewInt(1),
+					BscTransferOutAckRelayerFee: sdkmath.NewInt(0),
 				},
 				// this line is used by starport scaffolding # types/genesis/validField
 			},
diff --git a/x/bridge/types/params.go b/x/bridge/types/params.go
index acd938df2..84ff5b95d 100644
--- a/x/bridge/types/params.go
+++ b/x/bridge/types/params.go
@@ -7,26 +7,26 @@ import (
 )
 
 var (
-	DefaultTransferOutRelayerFeeParam    = sdkmath.NewInt(250000000000000) // 0.00025
-	DefaultTransferOutAckRelayerFeeParam = sdkmath.NewInt(0)
+	DefaultBscTransferOutRelayerFeeParam    = sdkmath.NewInt(250000000000000) // 0.00025
+	DefaultBscTransferOutAckRelayerFeeParam = sdkmath.NewInt(0)
 )
 
 // DefaultParams returns a default set of parameters
 func DefaultParams() Params {
 	return Params{
-		TransferOutRelayerFee:    DefaultTransferOutRelayerFeeParam,
-		TransferOutAckRelayerFee: DefaultTransferOutAckRelayerFeeParam,
+		BscTransferOutRelayerFee:    DefaultBscTransferOutRelayerFeeParam,
+		BscTransferOutAckRelayerFee: DefaultBscTransferOutAckRelayerFeeParam,
 	}
 }
 
 // Validate validates the set of params
 func (p Params) Validate() error {
-	err := validateRelayerFee(p.TransferOutRelayerFee)
+	err := validateRelayerFee(p.BscTransferOutRelayerFee)
 	if err != nil {
 		return err
 	}
 
-	err = validateRelayerFee(p.TransferOutAckRelayerFee)
+	err = validateRelayerFee(p.BscTransferOutAckRelayerFee)
 	if err != nil {
 		return err
 	}
diff --git a/x/bridge/types/params.pb.go b/x/bridge/types/params.pb.go
index 5cf1c5d0e..3c1b7a11d 100644
--- a/x/bridge/types/params.pb.go
+++ b/x/bridge/types/params.pb.go
@@ -27,10 +27,10 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
 
 // Params defines the parameters for the module.
 type Params struct {
-	// Relayer fee for the cross chain transfer out tx
-	TransferOutRelayerFee github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,1,opt,name=transfer_out_relayer_fee,json=transferOutRelayerFee,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"transfer_out_relayer_fee"`
-	// Relayer fee for the ACK or FAIL_ACK package of the cross chain transfer out tx
-	TransferOutAckRelayerFee github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,2,opt,name=transfer_out_ack_relayer_fee,json=transferOutAckRelayerFee,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"transfer_out_ack_relayer_fee"`
+	// Relayer fee for the cross chain transfer out tx to bsc
+	BscTransferOutRelayerFee github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,1,opt,name=bsc_transfer_out_relayer_fee,json=bscTransferOutRelayerFee,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"bsc_transfer_out_relayer_fee"`
+	// Relayer fee for the ACK or FAIL_ACK package of the cross chain transfer out tx to bsc
+	BscTransferOutAckRelayerFee github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,2,opt,name=bsc_transfer_out_ack_relayer_fee,json=bscTransferOutAckRelayerFee,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"bsc_transfer_out_ack_relayer_fee"`
 }
 
 func (m *Params) Reset()         { *m = Params{} }
@@ -73,24 +73,25 @@ func init() {
 func init() { proto.RegisterFile("greenfield/bridge/params.proto", fileDescriptor_0968257d902d40e4) }
 
 var fileDescriptor_0968257d902d40e4 = []byte{
-	// 272 bytes of a gzipped FileDescriptorProto
+	// 281 bytes of a gzipped FileDescriptorProto
 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4b, 0x2f, 0x4a, 0x4d,
 	0xcd, 0x4b, 0xcb, 0x4c, 0xcd, 0x49, 0xd1, 0x4f, 0x2a, 0xca, 0x4c, 0x49, 0x4f, 0xd5, 0x2f, 0x48,
 	0x2c, 0x4a, 0xcc, 0x2d, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x44, 0xc8, 0xeb, 0x41,
 	0xe4, 0xa5, 0x24, 0x93, 0xf3, 0x8b, 0x73, 0xf3, 0x8b, 0xe3, 0xc1, 0x0a, 0xf4, 0x21, 0x1c, 0x88,
-	0x6a, 0x29, 0x91, 0xf4, 0xfc, 0xf4, 0x7c, 0x88, 0x38, 0x88, 0x05, 0x11, 0x55, 0xfa, 0xcb, 0xc8,
-	0xc5, 0x16, 0x00, 0x36, 0x54, 0xa8, 0x94, 0x4b, 0xa2, 0xa4, 0x28, 0x31, 0xaf, 0x38, 0x2d, 0xb5,
-	0x28, 0x3e, 0xbf, 0xb4, 0x24, 0xbe, 0x28, 0x35, 0x27, 0xb1, 0x32, 0xb5, 0x28, 0x3e, 0x2d, 0x35,
-	0x55, 0x82, 0x51, 0x81, 0x51, 0x83, 0xd3, 0xc9, 0xe6, 0xc4, 0x3d, 0x79, 0x86, 0x5b, 0xf7, 0xe4,
-	0xd5, 0xd2, 0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, 0xa1, 0x76, 0x40, 0x29, 0xdd,
-	0xe2, 0x94, 0x6c, 0xfd, 0x92, 0xca, 0x82, 0xd4, 0x62, 0x3d, 0xcf, 0xbc, 0x92, 0x4b, 0x5b, 0x74,
-	0xb9, 0xa0, 0x4e, 0xf0, 0xcc, 0x2b, 0x09, 0x12, 0x85, 0x99, 0xee, 0x5f, 0x5a, 0x12, 0x04, 0x31,
-	0xdb, 0x2d, 0x35, 0x55, 0xa8, 0x86, 0x4b, 0x06, 0xc5, 0xda, 0xc4, 0xe4, 0x6c, 0x14, 0xab, 0x99,
-	0xa8, 0x60, 0xb5, 0x04, 0x92, 0xd5, 0x8e, 0xc9, 0xd9, 0x08, 0xdb, 0x9d, 0x3c, 0x4e, 0x3c, 0x92,
-	0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x09, 0x8f, 0xe5, 0x18, 0x2e, 0x3c,
-	0x96, 0x63, 0xb8, 0xf1, 0x58, 0x8e, 0x21, 0x4a, 0x0f, 0xc9, 0xa6, 0xa4, 0xbc, 0x24, 0xdd, 0xe4,
-	0x8c, 0xc4, 0xcc, 0x3c, 0x7d, 0xa4, 0x28, 0xa9, 0x80, 0x45, 0x0a, 0xd8, 0xd6, 0x24, 0x36, 0x70,
-	0x80, 0x1a, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x1e, 0xe7, 0x87, 0x06, 0xb6, 0x01, 0x00, 0x00,
+	0x6a, 0x29, 0x91, 0xf4, 0xfc, 0xf4, 0x7c, 0x88, 0x38, 0x88, 0x05, 0x11, 0x55, 0xea, 0x61, 0xe2,
+	0x62, 0x0b, 0x00, 0x1b, 0x2a, 0x54, 0xc3, 0x25, 0x93, 0x54, 0x9c, 0x1c, 0x5f, 0x52, 0x94, 0x98,
+	0x57, 0x9c, 0x96, 0x5a, 0x14, 0x9f, 0x5f, 0x5a, 0x12, 0x5f, 0x94, 0x9a, 0x93, 0x58, 0x99, 0x5a,
+	0x14, 0x9f, 0x96, 0x9a, 0x2a, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0xe9, 0x64, 0x73, 0xe2, 0x9e, 0x3c,
+	0xc3, 0xad, 0x7b, 0xf2, 0x6a, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0x50,
+	0x7b, 0xa0, 0x94, 0x6e, 0x71, 0x4a, 0xb6, 0x7e, 0x49, 0x65, 0x41, 0x6a, 0xb1, 0x9e, 0x67, 0x5e,
+	0xc9, 0xa5, 0x2d, 0xba, 0x5c, 0x50, 0x67, 0x78, 0xe6, 0x95, 0x04, 0x49, 0x24, 0x15, 0x27, 0x87,
+	0x40, 0x2d, 0xf0, 0x2f, 0x2d, 0x09, 0x82, 0x18, 0xef, 0x96, 0x9a, 0x2a, 0xd4, 0xcc, 0xc8, 0xa5,
+	0x80, 0x61, 0x7d, 0x62, 0x72, 0x36, 0x8a, 0x13, 0x98, 0xa8, 0xe0, 0x04, 0x69, 0x54, 0x27, 0x38,
+	0x26, 0x67, 0x23, 0x5c, 0xe1, 0xe4, 0x71, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f,
+	0x1e, 0xc9, 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c,
+	0x51, 0x7a, 0x48, 0x96, 0x25, 0xe5, 0x25, 0xe9, 0x26, 0x67, 0x24, 0x66, 0xe6, 0xe9, 0x23, 0xc5,
+	0x50, 0x05, 0x2c, 0x8e, 0xc0, 0x16, 0x27, 0xb1, 0x81, 0xc3, 0xd7, 0x18, 0x10, 0x00, 0x00, 0xff,
+	0xff, 0x90, 0x67, 0x42, 0x01, 0xc5, 0x01, 0x00, 0x00,
 }
 
 func (m *Params) Marshal() (dAtA []byte, err error) {
@@ -114,9 +115,9 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	var l int
 	_ = l
 	{
-		size := m.TransferOutAckRelayerFee.Size()
+		size := m.BscTransferOutAckRelayerFee.Size()
 		i -= size
-		if _, err := m.TransferOutAckRelayerFee.MarshalTo(dAtA[i:]); err != nil {
+		if _, err := m.BscTransferOutAckRelayerFee.MarshalTo(dAtA[i:]); err != nil {
 			return 0, err
 		}
 		i = encodeVarintParams(dAtA, i, uint64(size))
@@ -124,9 +125,9 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	i--
 	dAtA[i] = 0x12
 	{
-		size := m.TransferOutRelayerFee.Size()
+		size := m.BscTransferOutRelayerFee.Size()
 		i -= size
-		if _, err := m.TransferOutRelayerFee.MarshalTo(dAtA[i:]); err != nil {
+		if _, err := m.BscTransferOutRelayerFee.MarshalTo(dAtA[i:]); err != nil {
 			return 0, err
 		}
 		i = encodeVarintParams(dAtA, i, uint64(size))
@@ -153,9 +154,9 @@ func (m *Params) Size() (n int) {
 	}
 	var l int
 	_ = l
-	l = m.TransferOutRelayerFee.Size()
+	l = m.BscTransferOutRelayerFee.Size()
 	n += 1 + l + sovParams(uint64(l))
-	l = m.TransferOutAckRelayerFee.Size()
+	l = m.BscTransferOutAckRelayerFee.Size()
 	n += 1 + l + sovParams(uint64(l))
 	return n
 }
@@ -197,7 +198,7 @@ func (m *Params) Unmarshal(dAtA []byte) error {
 		switch fieldNum {
 		case 1:
 			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field TransferOutRelayerFee", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field BscTransferOutRelayerFee", wireType)
 			}
 			var stringLen uint64
 			for shift := uint(0); ; shift += 7 {
@@ -225,13 +226,13 @@ func (m *Params) Unmarshal(dAtA []byte) error {
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			if err := m.TransferOutRelayerFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+			if err := m.BscTransferOutRelayerFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
 				return err
 			}
 			iNdEx = postIndex
 		case 2:
 			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field TransferOutAckRelayerFee", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field BscTransferOutAckRelayerFee", wireType)
 			}
 			var stringLen uint64
 			for shift := uint(0); ; shift += 7 {
@@ -259,7 +260,7 @@ func (m *Params) Unmarshal(dAtA []byte) error {
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			if err := m.TransferOutAckRelayerFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+			if err := m.BscTransferOutAckRelayerFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
 				return err
 			}
 			iNdEx = postIndex
diff --git a/x/bridge/types/types.go b/x/bridge/types/types.go
index 6fe21334a..97046a951 100644
--- a/x/bridge/types/types.go
+++ b/x/bridge/types/types.go
@@ -4,9 +4,10 @@ import (
 	"math/big"
 
 	"cosmossdk.io/errors"
-	"github.com/cosmos/cosmos-sdk/bsc/rlp"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	"github.com/cosmos/cosmos-sdk/x/gov/types"
+	"github.com/ethereum/go-ethereum/accounts/abi"
+	"github.com/ethereum/go-ethereum/common"
 )
 
 const (
@@ -18,17 +19,61 @@ const (
 	SyncParamsChannelID                = types.SyncParamsChannelID
 )
 
+func SafeBigInt(input *big.Int) *big.Int {
+	if input == nil {
+		return big.NewInt(0)
+	}
+	return input
+}
+
 type TransferOutSynPackage struct {
 	Amount        *big.Int
 	Recipient     sdk.AccAddress
 	RefundAddress sdk.AccAddress
 }
 
+type TransferOutSynPackageStruct struct {
+	Amount        *big.Int
+	Recipient     common.Address
+	RefundAddress common.Address
+}
+
+var (
+	transferOutSynPackageType, _ = abi.NewType("tuple", "", []abi.ArgumentMarshaling{
+		{Name: "Amount", Type: "uint256"},
+		{Name: "Recipient", Type: "address"},
+		{Name: "RefundAddress", Type: "address"},
+	})
+
+	transferOutSynPackageArgs = abi.Arguments{
+		{Type: transferOutSynPackageType},
+	}
+)
+
+func (pkg *TransferOutSynPackage) Serialize() ([]byte, error) {
+	return transferOutSynPackageArgs.Pack(&TransferOutSynPackageStruct{
+		SafeBigInt(pkg.Amount),
+		common.BytesToAddress(pkg.Recipient),
+		common.BytesToAddress(pkg.RefundAddress),
+	})
+}
+
 func DeserializeTransferOutSynPackage(serializedPackage []byte) (*TransferOutSynPackage, error) {
-	var tp TransferOutSynPackage
-	err := rlp.DecodeBytes(serializedPackage, &tp)
+	unpacked, err := transferOutSynPackageArgs.Unpack(serializedPackage)
 	if err != nil {
-		return nil, errors.Wrapf(ErrInvalidPackage, "deserialize transfer out package failed")
+		return nil, errors.Wrapf(ErrInvalidPackage, "deserialize transfer out sync package failed")
+	}
+
+	unpackedStruct := abi.ConvertType(unpacked[0], TransferOutSynPackageStruct{})
+	pkgStruct, ok := unpackedStruct.(TransferOutSynPackageStruct)
+	if !ok {
+		return nil, errors.Wrapf(ErrInvalidPackage, "reflect transfer out sync package failed")
+	}
+
+	tp := TransferOutSynPackage{
+		pkgStruct.Amount,
+		pkgStruct.Recipient.Bytes(),
+		pkgStruct.RefundAddress.Bytes(),
 	}
 	return &tp, nil
 }
@@ -39,12 +84,49 @@ type TransferOutRefundPackage struct {
 	RefundReason uint32
 }
 
+type TransferOutRefundPackageStruct struct {
+	RefundAmount *big.Int
+	RefundAddr   common.Address
+	RefundReason uint32
+}
+
+var (
+	transferOutRefundPackageType, _ = abi.NewType("tuple", "", []abi.ArgumentMarshaling{
+		{Name: "RefundAmount", Type: "uint256"},
+		{Name: "RefundAddr", Type: "address"},
+		{Name: "RefundReason", Type: "uint32"},
+	})
+
+	transferOutRefundPackageArgs = abi.Arguments{
+		{Type: transferOutRefundPackageType},
+	}
+)
+
+func (pkg *TransferOutRefundPackage) Serialize() ([]byte, error) {
+	return transferOutRefundPackageArgs.Pack(&TransferOutRefundPackageStruct{
+		SafeBigInt(pkg.RefundAmount),
+		common.BytesToAddress(pkg.RefundAddr),
+		pkg.RefundReason,
+	})
+}
+
 func DeserializeTransferOutRefundPackage(serializedPackage []byte) (*TransferOutRefundPackage, error) {
-	var tp TransferOutRefundPackage
-	err := rlp.DecodeBytes(serializedPackage, &tp)
+	unpacked, err := transferOutRefundPackageArgs.Unpack(serializedPackage)
 	if err != nil {
 		return nil, errors.Wrapf(ErrInvalidPackage, "deserialize transfer out refund package failed")
 	}
+
+	unpackedStruct := abi.ConvertType(unpacked[0], TransferOutRefundPackageStruct{})
+	pkgStruct, ok := unpackedStruct.(TransferOutRefundPackageStruct)
+	if !ok {
+		return nil, errors.Wrapf(ErrInvalidPackage, "reflect transfer out refund package failed")
+	}
+
+	tp := TransferOutRefundPackage{
+		pkgStruct.RefundAmount,
+		pkgStruct.RefundAddr.Bytes(),
+		pkgStruct.RefundReason,
+	}
 	return &tp, nil
 }
 
@@ -54,12 +136,48 @@ type TransferInSynPackage struct {
 	RefundAddress   sdk.AccAddress
 }
 
+type TransferInSynPackageStruct struct {
+	Amount          *big.Int
+	ReceiverAddress common.Address
+	RefundAddress   common.Address
+}
+
+var (
+	transferInSynPackageType, _ = abi.NewType("tuple", "", []abi.ArgumentMarshaling{
+		{Name: "Amount", Type: "uint256"},
+		{Name: "ReceiverAddress", Type: "address"},
+		{Name: "RefundAddress", Type: "address"},
+	})
+
+	transferInSynPackageArgs = abi.Arguments{
+		{Type: transferInSynPackageType},
+	}
+)
+
+func (pkg *TransferInSynPackage) Serialize() ([]byte, error) {
+	return transferInSynPackageArgs.Pack(&TransferInSynPackageStruct{
+		SafeBigInt(pkg.Amount),
+		common.BytesToAddress(pkg.ReceiverAddress),
+		common.BytesToAddress(pkg.RefundAddress),
+	})
+}
+
 func DeserializeTransferInSynPackage(serializedPackage []byte) (*TransferInSynPackage, error) {
-	var tp TransferInSynPackage
-	err := rlp.DecodeBytes(serializedPackage, &tp)
+	unpacked, err := transferInSynPackageArgs.Unpack(serializedPackage)
 	if err != nil {
-		return nil, errors.Wrapf(ErrInvalidPackage, "deserialize transfer in package failed")
+		return nil, errors.Wrapf(ErrInvalidPackage, "deserialize transfer in sync package failed")
+	}
+
+	unpackedStruct := abi.ConvertType(unpacked[0], TransferInSynPackageStruct{})
+	pkgStruct, ok := unpackedStruct.(TransferInSynPackageStruct)
+	if !ok {
+		return nil, errors.Wrapf(ErrInvalidPackage, "reflect transfer in sync package failed")
+	}
 
+	tp := TransferInSynPackage{
+		pkgStruct.Amount,
+		pkgStruct.ReceiverAddress.Bytes(),
+		pkgStruct.RefundAddress.Bytes(),
 	}
 	return &tp, nil
 }
@@ -69,3 +187,29 @@ type TransferInRefundPackage struct {
 	RefundAddress sdk.AccAddress
 	RefundReason  uint32
 }
+
+type TransferInRefundPackageStruct struct {
+	RefundAmount  *big.Int
+	RefundAddress common.Address
+	RefundReason  uint32
+}
+
+var (
+	transferInRefundPackageType, _ = abi.NewType("tuple", "", []abi.ArgumentMarshaling{
+		{Name: "RefundAmount", Type: "uint256"},
+		{Name: "RefundAddr", Type: "address"},
+		{Name: "RefundReason", Type: "uint32"},
+	})
+
+	transferInRefundPackageArgs = abi.Arguments{
+		{Type: transferInRefundPackageType},
+	}
+)
+
+func (pkg *TransferInRefundPackage) Serialize() ([]byte, error) {
+	return transferInRefundPackageArgs.Pack(&TransferInRefundPackageStruct{
+		SafeBigInt(pkg.RefundAmount),
+		common.BytesToAddress(pkg.RefundAddress),
+		pkg.RefundReason,
+	})
+}
diff --git a/x/challenge/abci.go b/x/challenge/abci.go
index cfc4449be..f300ede0c 100644
--- a/x/challenge/abci.go
+++ b/x/challenge/abci.go
@@ -64,36 +64,38 @@ func EndBlocker(ctx sdk.Context, keeper k.Keeper) {
 		}
 
 		// random redundancy index (sp address)
-		var spOperatorAddress string
-		secondarySpAddresses := objectInfo.SecondarySpAddresses
+		var spOperatorId uint32
 
-		redundancyIndex := k.RandomRedundancyIndex(seed, uint64(len(secondarySpAddresses)+1))
+		bucket, found := keeper.StorageKeeper.GetBucketInfo(ctx, objectInfo.BucketName)
+		if !found {
+			continue
+		}
+		gvg, found := keeper.StorageKeeper.GetObjectGVG(ctx, bucket.Id, objectInfo.LocalVirtualGroupId)
+		if !found {
+			continue
+		}
+		redundancyIndex := k.RandomRedundancyIndex(seed, uint64(len(gvg.SecondarySpIds)+1))
 		if redundancyIndex == types.RedundancyIndexPrimary { // primary sp
-			bucket, found := keeper.StorageKeeper.GetBucketInfo(ctx, objectInfo.BucketName)
-			if !found {
-				continue
-			}
-			spOperatorAddress = bucket.PrimarySpAddress
+			spOperatorId = bucket.PrimarySpId
 		} else {
-			spOperatorAddress = secondarySpAddresses[redundancyIndex]
+			spOperatorId = gvg.SecondarySpIds[redundancyIndex]
 		}
 
-		spOperatorAddr, err := sdk.AccAddressFromHexUnsafe(spOperatorAddress)
-		if err != nil {
+		sp, found := keeper.SpKeeper.GetStorageProvider(ctx, spOperatorId)
+		if !found {
 			continue
 		}
-		sp, found := keeper.SpKeeper.GetStorageProvider(ctx, spOperatorAddr)
-		if !found || sp.Status != sptypes.STATUS_IN_SERVICE {
+		if sp.Status != sptypes.STATUS_IN_SERVICE && sp.Status != sptypes.STATUS_GRACEFUL_EXITING {
 			continue
 		}
 
-		mapKey := fmt.Sprintf("%s-%s", spOperatorAddress, objectInfo.Id.String())
+		mapKey := fmt.Sprintf("%d-%s", spOperatorId, objectInfo.Id.String())
 		if _, ok := objectMap[mapKey]; ok { // already generated for this pair
 			continue
 		}
 
 		// check recent slash
-		if keeper.ExistsSlash(ctx, spOperatorAddr, objectInfo.Id) {
+		if keeper.ExistsSlash(ctx, sp.Id, objectInfo.Id) {
 			continue
 		}
 
@@ -112,7 +114,7 @@ func EndBlocker(ctx sdk.Context, keeper k.Keeper) {
 			ChallengeId:       challengeId,
 			ObjectId:          objectInfo.Id,
 			SegmentIndex:      segmentIndex,
-			SpOperatorAddress: spOperatorAddress,
+			SpOperatorAddress: sp.OperatorAddress,
 			RedundancyIndex:   redundancyIndex,
 			ChallengerAddress: "",
 			ExpiredHeight:     expiredHeight,
diff --git a/x/challenge/keeper/bls_signed_msg.go b/x/challenge/keeper/bls_signed_msg.go
index 28be92bb2..782321e74 100644
--- a/x/challenge/keeper/bls_signed_msg.go
+++ b/x/challenge/keeper/bls_signed_msg.go
@@ -5,6 +5,7 @@ import (
 
 	"cosmossdk.io/errors"
 	"github.com/bits-and-blooms/bitset"
+	sdk "github.com/cosmos/cosmos-sdk/types"
 	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
 	"github.com/prysmaticlabs/prysm/crypto/bls"
 
@@ -14,7 +15,7 @@ import (
 // BlsSignedMsg defined the interface of a bls signed message.
 type BlsSignedMsg interface {
 	// GetBlsSignBytes returns the bls signed message in bytes.
-	GetBlsSignBytes() [32]byte
+	GetBlsSignBytes(chainId string) [32]byte
 
 	// GetVoteValidatorSet returns the validators who signed the message.
 	GetVoteValidatorSet() []uint64
@@ -24,7 +25,7 @@ type BlsSignedMsg interface {
 }
 
 // verifySignature verifies whether the signature is valid or not.
-func (k Keeper) verifySignature(signedMsg BlsSignedMsg, validators []stakingtypes.Validator) ([]string, error) {
+func (k Keeper) verifySignature(ctx sdk.Context, signedMsg BlsSignedMsg, validators []stakingtypes.Validator) ([]string, error) {
 	validatorsBitSet := bitset.From(signedMsg.GetVoteValidatorSet())
 	if validatorsBitSet.Count() > uint(len(validators)) {
 		return nil, errors.Wrap(types.ErrInvalidVoteValidatorSet, "number of validator set is larger than validators")
@@ -54,7 +55,7 @@ func (k Keeper) verifySignature(signedMsg BlsSignedMsg, validators []stakingtype
 		return nil, errors.Wrapf(types.ErrInvalidVoteAggSignature, fmt.Sprintf("BLS signature converts failed: %v", err))
 	}
 
-	if !aggSig.FastAggregateVerify(votedPubKeys, signedMsg.GetBlsSignBytes()) {
+	if !aggSig.FastAggregateVerify(votedPubKeys, signedMsg.GetBlsSignBytes(ctx.ChainID())) {
 		return nil, errors.Wrap(types.ErrInvalidVoteAggSignature, "Signature verify failed")
 	}
 
diff --git a/x/challenge/keeper/msg_server_attest.go b/x/challenge/keeper/msg_server_attest.go
index b844a2e3f..6d3e8fad7 100644
--- a/x/challenge/keeper/msg_server_attest.go
+++ b/x/challenge/keeper/msg_server_attest.go
@@ -25,6 +25,11 @@ func (k msgServer) Attest(goCtx context.Context, msg *types.MsgAttest) (*types.M
 	submitter := sdk.MustAccAddressFromHex(msg.Submitter)
 	spOperator := sdk.MustAccAddressFromHex(msg.SpOperatorAddress)
 
+	sp, found := k.SpKeeper.GetStorageProviderByOperatorAddr(ctx, spOperator)
+	if !found {
+		return nil, errors.Wrapf(types.ErrUnknownSp, "cannot find sp with operator address: %s", msg.SpOperatorAddress)
+	}
+
 	challenger := sdk.AccAddress{}
 	if msg.ChallengerAddress != "" {
 		challenger = sdk.MustAccAddressFromHex(msg.ChallengerAddress)
@@ -47,35 +52,51 @@ func (k msgServer) Attest(goCtx context.Context, msg *types.MsgAttest) (*types.M
 		return nil, types.ErrNotInturnChallenger
 	}
 
+	// check attest validators and signatures
+	validators, err := k.verifySignature(ctx, msg, allValidators)
+	if err != nil {
+		return nil, err
+	}
+
 	//check object, and get object info
 	objectInfo, found := k.StorageKeeper.GetObjectInfoById(ctx, msg.ObjectId)
 	if !found { // be noted: even the object info is not in service now, we will continue slash the storage provider
-		return nil, types.ErrUnknownObject
+		return nil, types.ErrUnknownBucketObject
 	}
 
-	// check attest validators and signatures
-	validators, err := k.verifySignature(msg, allValidators)
-	if err != nil {
-		return nil, err
+	//for migrating buckets, or swapping out, the process could be done when offline service does the verification
+	bucketInfo, _ := k.StorageKeeper.GetBucketInfo(ctx, objectInfo.BucketName)
+	if bucketInfo.PrimarySpId != sp.Id {
+		gvg, _ := k.StorageKeeper.GetObjectGVG(ctx, bucketInfo.Id, objectInfo.LocalVirtualGroupId)
+		found = false
+		for _, id := range gvg.SecondarySpIds {
+			if id == sp.Id {
+				found = true
+				break
+			}
+		}
+		if !found {
+			return nil, errors.Wrapf(types.ErrNotStoredOnSp, "sp %s does not store the object anymore", sp.OperatorAddress)
+		}
 	}
 
 	if msg.VoteResult == types.CHALLENGE_SUCCEED {
 		// check slash
-		if k.ExistsSlash(ctx, spOperator, msg.ObjectId) {
+		if k.ExistsSlash(ctx, sp.Id, msg.ObjectId) {
 			return nil, types.ErrDuplicatedSlash
 		}
 
 		// do slash & reward
 		objectSize := objectInfo.PayloadSize
-		err = k.doSlashAndRewards(ctx, msg.ChallengeId, msg.VoteResult, objectSize, spOperator, submitter, challenger, validators)
+		err = k.doSlashAndRewards(ctx, msg.ChallengeId, msg.VoteResult, objectSize, sp.Id, submitter, challenger, validators)
 		if err != nil {
 			return nil, err
 		}
 
 		slash := types.Slash{
-			SpOperatorAddress: spOperator,
-			ObjectId:          msg.ObjectId,
-			Height:            uint64(ctx.BlockHeight()),
+			SpId:     sp.Id,
+			ObjectId: msg.ObjectId,
+			Height:   uint64(ctx.BlockHeight()),
 		}
 		k.SaveSlash(ctx, slash)
 	} else {
@@ -86,7 +107,7 @@ func (k msgServer) Attest(goCtx context.Context, msg *types.MsgAttest) (*types.M
 		}
 
 		// reward validators & tx submitter
-		err = k.doHeartbeatAndRewards(ctx, msg.ChallengeId, msg.VoteResult, spOperator, submitter, challenger)
+		err = k.doHeartbeatAndRewards(ctx, msg.ChallengeId, msg.VoteResult, sp.Id, submitter, challenger)
 		if err != nil {
 			return nil, err
 		}
@@ -151,7 +172,7 @@ func (k msgServer) calculateSlashRewards(ctx sdk.Context, total sdkmath.Int, cha
 
 // doSlashAndRewards will execute the slash, transfer the rewards and emit events.
 func (k msgServer) doSlashAndRewards(ctx sdk.Context, challengeId uint64, voteResult types.VoteResult, objectSize uint64,
-	spOperator, submitter, challenger sdk.AccAddress, validators []string) error {
+	spID uint32, submitter, challenger sdk.AccAddress, validators []string) error {
 
 	slashAmount := k.calculateSlashAmount(ctx, objectSize)
 	challengerReward, eachValidatorReward, submitterReward := k.calculateSlashRewards(ctx, slashAmount,
@@ -184,7 +205,7 @@ func (k msgServer) doSlashAndRewards(ctx sdk.Context, challengeId uint64, voteRe
 			Amount: submitterReward,
 		}})
 
-	err := k.SpKeeper.Slash(ctx, spOperator, rewards)
+	err := k.SpKeeper.Slash(ctx, spID, rewards)
 	if err != nil {
 		return err
 	}
@@ -192,7 +213,7 @@ func (k msgServer) doSlashAndRewards(ctx sdk.Context, challengeId uint64, voteRe
 	event := types.EventAttestChallenge{
 		ChallengeId:            challengeId,
 		Result:                 voteResult,
-		SpOperatorAddress:      spOperator.String(),
+		SpId:                   spID,
 		SlashAmount:            slashAmount.String(),
 		ChallengerAddress:      challenger.String(),
 		ChallengerRewardAmount: challengerReward.String(),
@@ -217,7 +238,7 @@ func (k msgServer) calculateHeartbeatRewards(ctx sdk.Context, total sdkmath.Int)
 
 // doHeartbeatAndRewards will transfer the tax to distribution account and rewards to submitter.
 func (k msgServer) doHeartbeatAndRewards(ctx sdk.Context, challengeId uint64, voteResult types.VoteResult,
-	spOperator, submitter, challenger sdk.AccAddress) error {
+	spID uint32, submitter, challenger sdk.AccAddress) error {
 	totalAmount, err := k.paymentKeeper.QueryDynamicBalance(ctx, paymentmoduletypes.ValidatorTaxPoolAddress)
 	if err != nil {
 		return err
@@ -243,7 +264,7 @@ func (k msgServer) doHeartbeatAndRewards(ctx sdk.Context, challengeId uint64, vo
 	return ctx.EventManager().EmitTypedEvents(&types.EventAttestChallenge{
 		ChallengeId:            challengeId,
 		Result:                 voteResult,
-		SpOperatorAddress:      spOperator.String(),
+		SpId:                   spID,
 		SlashAmount:            "",
 		ChallengerAddress:      challenger.String(),
 		ChallengerRewardAmount: "",
diff --git a/x/challenge/keeper/msg_server_attest_test.go b/x/challenge/keeper/msg_server_attest_test.go
index 3d6282a67..602832673 100644
--- a/x/challenge/keeper/msg_server_attest_test.go
+++ b/x/challenge/keeper/msg_server_attest_test.go
@@ -12,7 +12,9 @@ import (
 
 	"github.com/bnb-chain/greenfield/testutil/sample"
 	"github.com/bnb-chain/greenfield/x/challenge/types"
+	sptypes "github.com/bnb-chain/greenfield/x/sp/types"
 	storagetypes "github.com/bnb-chain/greenfield/x/storage/types"
+	virtualgrouptypes "github.com/bnb-chain/greenfield/x/virtualgroup/types"
 )
 
 func (s *TestSuite) TestAttest_Invalid() {
@@ -43,6 +45,11 @@ func (s *TestSuite) TestAttest_Invalid() {
 	s.storageKeeper.EXPECT().GetObjectInfoById(gomock.Any(), gomock.Eq(math.NewUint(10))).
 		Return(existObject, true).AnyTimes()
 
+	spOperatorAcc := sample.RandAccAddress()
+	sp := &sptypes.StorageProvider{Id: 10, OperatorAddress: spOperatorAcc.String()}
+	s.spKeeper.EXPECT().GetStorageProviderByOperatorAddr(gomock.Any(), gomock.Any()).
+		Return(sp, true).AnyTimes()
+
 	tests := []struct {
 		name string
 		msg  types.MsgAttest
@@ -123,10 +130,18 @@ func (s *TestSuite) TestAttest_Heartbeat() {
 	s.stakingKeeper.EXPECT().GetHistoricalInfo(gomock.Any(), gomock.Any()).
 		Return(historicalInfo, true).AnyTimes()
 
-	existObjectName := "existobject"
+	existBucket := &storagetypes.BucketInfo{
+		Id:          math.NewUint(10),
+		BucketName:  "existbucket",
+		PrimarySpId: 1,
+	}
+	s.storageKeeper.EXPECT().GetBucketInfo(gomock.Any(), gomock.Eq(existBucket.BucketName)).
+		Return(existBucket, true).AnyTimes()
+
 	existObject := &storagetypes.ObjectInfo{
 		Id:           math.NewUint(10),
-		ObjectName:   existObjectName,
+		ObjectName:   "existobject",
+		BucketName:   existBucket.BucketName,
 		ObjectStatus: storagetypes.OBJECT_STATUS_SEALED,
 		PayloadSize:  500}
 	s.storageKeeper.EXPECT().GetObjectInfoById(gomock.Any(), gomock.Eq(math.NewUint(10))).
@@ -138,16 +153,26 @@ func (s *TestSuite) TestAttest_Heartbeat() {
 		Return(nil).AnyTimes()
 
 	spOperatorAcc := sample.RandAccAddress()
+	sp := &sptypes.StorageProvider{Id: 10, OperatorAddress: spOperatorAcc.String()}
+	s.spKeeper.EXPECT().GetStorageProviderByOperatorAddr(gomock.Any(), gomock.Any()).
+		Return(sp, true).AnyTimes()
+
+	gvg := &virtualgrouptypes.GlobalVirtualGroup{
+		SecondarySpIds: []uint32{10},
+	}
+	s.storageKeeper.EXPECT().GetObjectGVG(gomock.Any(), gomock.Eq(existBucket.Id), gomock.Any()).
+		Return(gvg, true).AnyTimes()
+
 	attestMsg := &types.MsgAttest{
 		Submitter:         validSubmitter.String(),
 		ChallengeId:       challengeId,
 		ObjectId:          math.NewUint(10),
-		SpOperatorAddress: spOperatorAcc.String(),
+		SpOperatorAddress: sp.OperatorAddress,
 		VoteResult:        types.CHALLENGE_FAILED,
 		ChallengerAddress: "",
 		VoteValidatorSet:  []uint64{1},
 	}
-	toSign := attestMsg.GetBlsSignBytes()
+	toSign := attestMsg.GetBlsSignBytes(s.ctx.ChainID())
 
 	voteAggSignature := blsKey.Sign(toSign[:])
 	attestMsg.VoteAggSignature = voteAggSignature.Marshal()
@@ -185,21 +210,32 @@ func (s *TestSuite) TestAttest_Normal() {
 	s.stakingKeeper.EXPECT().GetHistoricalInfo(gomock.Any(), gomock.Any()).
 		Return(historicalInfo, true).AnyTimes()
 
-	existObjectName := "existobject"
+	existBucket := &storagetypes.BucketInfo{
+		Id:          math.NewUint(10),
+		BucketName:  "existbucket",
+		PrimarySpId: 1,
+	}
+	s.storageKeeper.EXPECT().GetBucketInfo(gomock.Any(), gomock.Eq(existBucket.BucketName)).
+		Return(existBucket, true).AnyTimes()
+
 	existObject := &storagetypes.ObjectInfo{
 		Id:           math.NewUint(10),
-		ObjectName:   existObjectName,
+		ObjectName:   "existobject",
+		BucketName:   existBucket.BucketName,
 		ObjectStatus: storagetypes.OBJECT_STATUS_SEALED,
 		PayloadSize:  500}
 	s.storageKeeper.EXPECT().GetObjectInfoById(gomock.Any(), gomock.Eq(math.NewUint(10))).
 		Return(existObject, true).AnyTimes()
 
+	spOperatorAcc := sample.RandAccAddress()
+	sp := &sptypes.StorageProvider{Id: 1, OperatorAddress: spOperatorAcc.String()}
 	s.spKeeper.EXPECT().DepositDenomForSP(gomock.Any()).
 		Return("BNB").AnyTimes()
 	s.spKeeper.EXPECT().Slash(gomock.Any(), gomock.Any(), gomock.Any()).
 		Return(nil).AnyTimes()
+	s.spKeeper.EXPECT().GetStorageProviderByOperatorAddr(gomock.Any(), gomock.Any()).
+		Return(sp, true).AnyTimes()
 
-	spOperatorAcc := sample.RandAccAddress()
 	attestMsg := &types.MsgAttest{
 		Submitter:         validSubmitter.String(),
 		ChallengeId:       challengeId,
@@ -209,7 +245,7 @@ func (s *TestSuite) TestAttest_Normal() {
 		ChallengerAddress: "",
 		VoteValidatorSet:  []uint64{1},
 	}
-	toSign := attestMsg.GetBlsSignBytes()
+	toSign := attestMsg.GetBlsSignBytes(s.ctx.ChainID())
 
 	voteAggSignature := blsKey.Sign(toSign[:])
 	attestMsg.VoteAggSignature = voteAggSignature.Marshal()
@@ -225,5 +261,5 @@ func (s *TestSuite) TestAttest_Normal() {
 		}
 	}
 	s.Require().True(found)
-	s.Require().True(s.challengeKeeper.ExistsSlash(s.ctx, spOperatorAcc, attestMsg.ObjectId))
+	s.Require().True(s.challengeKeeper.ExistsSlash(s.ctx, sp.Id, attestMsg.ObjectId))
 }
diff --git a/x/challenge/keeper/msg_server_submit.go b/x/challenge/keeper/msg_server_submit.go
index 95e521dfc..61f34b83c 100644
--- a/x/challenge/keeper/msg_server_submit.go
+++ b/x/challenge/keeper/msg_server_submit.go
@@ -3,6 +3,7 @@ package keeper
 import (
 	"context"
 
+	"cosmossdk.io/errors"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 
 	"github.com/bnb-chain/greenfield/x/challenge/types"
@@ -18,52 +19,68 @@ func (k msgServer) Submit(goCtx context.Context, msg *types.MsgSubmit) (*types.M
 	challenger := sdk.MustAccAddressFromHex(msg.Challenger)
 
 	// check sp status
-	sp, found := k.SpKeeper.GetStorageProvider(ctx, spOperator)
+	bucketInfo, found := k.StorageKeeper.GetBucketInfo(ctx, msg.BucketName)
+	if !found {
+		return nil, types.ErrUnknownBucketObject
+	}
+	sp, found := k.SpKeeper.GetStorageProvider(ctx, bucketInfo.PrimarySpId)
 	if !found {
 		return nil, types.ErrUnknownSp
 	}
-	if sp.Status != sptypes.STATUS_IN_SERVICE {
+	if sp.Status != sptypes.STATUS_IN_SERVICE && sp.Status != sptypes.STATUS_GRACEFUL_EXITING {
 		return nil, types.ErrInvalidSpStatus
 	}
 
 	// check object & read needed data
 	objectInfo, found := k.StorageKeeper.GetObjectInfo(ctx, msg.BucketName, msg.ObjectName)
 	if !found {
-		return nil, types.ErrUnknownObject
+		return nil, types.ErrUnknownBucketObject
 	}
 	if objectInfo.ObjectStatus != storagetypes.OBJECT_STATUS_SEALED {
 		return nil, types.ErrInvalidObjectStatus
 	}
 
-	// check whether the sp stores the object info
+	// check whether the sp stores the object info, generate redundancy index
 	stored := false
-	for _, sp := range objectInfo.GetSecondarySpAddresses() {
-		if spOperator.Equals(sdk.MustAccAddressFromHex(sp)) {
-			stored = true
-			break
-		}
+	redundancyIndex := types.RedundancyIndexPrimary
+
+	// check primary sp
+	tmpSp, found := k.SpKeeper.GetStorageProvider(ctx, bucketInfo.PrimarySpId)
+	if !found {
+		return nil, errors.Wrapf(types.ErrUnknownSp, "cannot find storage provider: %d", bucketInfo.PrimarySpId)
 	}
+	if spOperator.Equals(sdk.MustAccAddressFromHex(tmpSp.OperatorAddress)) {
+		stored = true
+	}
+
 	if !stored {
-		bucket, _ := k.StorageKeeper.GetBucketInfo(ctx, msg.BucketName)
-		if !spOperator.Equals(sdk.MustAccAddressFromHex(bucket.GetPrimarySpAddress())) {
-			return nil, types.ErrNotStoredOnSp
+		gvg, found := k.StorageKeeper.GetObjectGVG(ctx, bucketInfo.Id, objectInfo.LocalVirtualGroupId)
+		if !found {
+			return nil, errors.Wrapf(types.ErrCannotFindGVG, "no GVG binding for LVG: %d", objectInfo.LocalVirtualGroupId)
 		}
+
+		// check secondary sp
+		for i, spId := range gvg.SecondarySpIds {
+			tmpSp, found := k.SpKeeper.GetStorageProvider(ctx, spId)
+			if !found {
+				return nil, errors.Wrapf(types.ErrUnknownSp, "cannot find storage provider: %d", spId)
+			}
+			if spOperator.Equals(sdk.MustAccAddressFromHex(tmpSp.OperatorAddress)) {
+				redundancyIndex = int32(i)
+				stored = true
+				break
+			}
+		}
+	}
+	if !stored {
+		return nil, types.ErrNotStoredOnSp
 	}
 
 	// check sp recent slash
-	if k.ExistsSlash(ctx, spOperator, objectInfo.Id) {
+	if k.ExistsSlash(ctx, sp.Id, objectInfo.Id) {
 		return nil, types.ErrExistsRecentSlash
 	}
 
-	// generate redundancy index
-	redundancyIndex := types.RedundancyIndexPrimary
-	for i, sp := range objectInfo.GetSecondarySpAddresses() {
-		if spOperator.Equals(sdk.MustAccAddressFromHex(sp)) {
-			redundancyIndex = int32(i)
-			break
-		}
-	}
-
 	// generate segment index
 	segmentIndex := msg.SegmentIndex
 	segments := CalculateSegments(objectInfo.PayloadSize, k.Keeper.StorageKeeper.MaxSegmentSize(ctx))
diff --git a/x/challenge/keeper/msg_server_submit_test.go b/x/challenge/keeper/msg_server_submit_test.go
index 86f059553..45744abfe 100644
--- a/x/challenge/keeper/msg_server_submit_test.go
+++ b/x/challenge/keeper/msg_server_submit_test.go
@@ -11,15 +11,14 @@ import (
 	"github.com/bnb-chain/greenfield/x/challenge/types"
 	sptypes "github.com/bnb-chain/greenfield/x/sp/types"
 	storagetypes "github.com/bnb-chain/greenfield/x/storage/types"
+	virtualgrouptypes "github.com/bnb-chain/greenfield/x/virtualgroup/types"
 )
 
 func (s *TestSuite) TestSubmit() {
 	existSpAddr := sample.RandAccAddress()
-	existSp := &sptypes.StorageProvider{Status: sptypes.STATUS_IN_SERVICE}
-	s.spKeeper.EXPECT().GetStorageProvider(gomock.Any(), gomock.Eq(existSpAddr)).
+	existSp := &sptypes.StorageProvider{Status: sptypes.STATUS_IN_SERVICE, Id: 100, OperatorAddress: existSpAddr.String()}
+	s.spKeeper.EXPECT().GetStorageProvider(gomock.Any(), gomock.Eq(existSp.Id)).
 		Return(existSp, true).AnyTimes()
-	s.spKeeper.EXPECT().GetStorageProvider(gomock.Any(), gomock.Any()).
-		Return(nil, false).AnyTimes()
 
 	existBucketName, existObjectName := "existbucket", "existobject"
 	existObject := &storagetypes.ObjectInfo{
@@ -34,33 +33,42 @@ func (s *TestSuite) TestSubmit() {
 		Return(nil, false).AnyTimes()
 
 	existBucket := &storagetypes.BucketInfo{
-		BucketName:       existBucketName,
-		PrimarySpAddress: existSpAddr.String()}
+		BucketName:  existBucketName,
+		PrimarySpId: existSp.Id}
 	s.storageKeeper.EXPECT().GetBucketInfo(gomock.Any(), gomock.Eq(existBucketName)).
 		Return(existBucket, true).AnyTimes()
+	s.storageKeeper.EXPECT().GetBucketInfo(gomock.Any(), gomock.Any()).
+		Return(nil, false).AnyTimes()
 
 	s.storageKeeper.EXPECT().MaxSegmentSize(gomock.Any()).Return(uint64(10000)).AnyTimes()
 
+	gvg := &virtualgrouptypes.GlobalVirtualGroup{PrimarySpId: 100}
+	s.storageKeeper.EXPECT().GetObjectGVG(gomock.Any(), gomock.Any(), gomock.Any()).
+		Return(gvg, true).AnyTimes()
+
 	tests := []struct {
 		name string
 		msg  types.MsgSubmit
 		err  error
 	}{
 		{
-			name: "unknown sp",
+			name: "not store on the sp",
 			msg: types.MsgSubmit{
 				Challenger:        sample.AccAddress(),
 				SpOperatorAddress: sample.AccAddress(),
+				BucketName:        existBucketName,
+				ObjectName:        existObjectName,
 			},
-			err: types.ErrUnknownSp,
+			err: types.ErrNotStoredOnSp,
 		}, {
 			name: "unknown object",
 			msg: types.MsgSubmit{
 				Challenger:        sample.AccAddress(),
 				SpOperatorAddress: existSpAddr.String(),
+				BucketName:        existBucketName,
 				ObjectName:        "nonexistobject",
 			},
-			err: types.ErrUnknownObject,
+			err: types.ErrUnknownBucketObject,
 		},
 		{
 			name: "invalid segment index",
@@ -110,9 +118,9 @@ func (s *TestSuite) TestSubmit() {
 
 	// create slash
 	s.challengeKeeper.SaveSlash(s.ctx, types.Slash{
-		SpOperatorAddress: existSpAddr,
-		ObjectId:          existObject.Id,
-		Height:            100,
+		SpId:     existSp.Id,
+		ObjectId: existObject.Id,
+		Height:   100,
 	})
 
 	tests = []struct {
diff --git a/x/challenge/keeper/slash.go b/x/challenge/keeper/slash.go
index 62035361d..c11a9ddf7 100644
--- a/x/challenge/keeper/slash.go
+++ b/x/challenge/keeper/slash.go
@@ -18,7 +18,7 @@ func (k Keeper) SaveSlash(ctx sdk.Context, slash types.Slash) {
 	heightBytes := make([]byte, 8)
 	binary.BigEndian.PutUint64(heightBytes, slash.Height)
 
-	store.Set(getSlashKeyBytes(slash.SpOperatorAddress, slash.ObjectId), heightBytes)
+	store.Set(getSlashKeyBytes(slash.SpId, slash.ObjectId), heightBytes)
 }
 
 // RemoveSlashUntil removes slashes which are created earlier
@@ -36,14 +36,16 @@ func (k Keeper) RemoveSlashUntil(ctx sdk.Context, height uint64) {
 }
 
 // ExistsSlash check whether there exists recent slash for a pair of sp and object info or not
-func (k Keeper) ExistsSlash(ctx sdk.Context, spOperatorAddress sdk.AccAddress, objectId sdkmath.Uint) bool {
+func (k Keeper) ExistsSlash(ctx sdk.Context, spId uint32, objectId sdkmath.Uint) bool {
 	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.SlashKeyPrefix)
 
-	return store.Has(getSlashKeyBytes(spOperatorAddress, objectId))
+	return store.Has(getSlashKeyBytes(spId, objectId))
 }
 
 // getSlashKeyBytes returns the byte representation of Slash key
-func getSlashKeyBytes(spOperatorAddress sdk.AccAddress, objectId sdkmath.Uint) []byte {
-	allBytes := append(spOperatorAddress.Bytes(), objectId.Bytes()...)
+func getSlashKeyBytes(spId uint32, objectId sdkmath.Uint) []byte {
+	idBytes := make([]byte, 4)
+	binary.BigEndian.PutUint32(idBytes, spId)
+	allBytes := append(idBytes, objectId.Bytes()...)
 	return sdk.Keccak256(allBytes)
 }
diff --git a/x/challenge/keeper/slash_test.go b/x/challenge/keeper/slash_test.go
index 7a8835277..036a26338 100644
--- a/x/challenge/keeper/slash_test.go
+++ b/x/challenge/keeper/slash_test.go
@@ -1,7 +1,6 @@
 package keeper_test
 
 import (
-	"fmt"
 	"testing"
 
 	sdkmath "cosmossdk.io/math"
@@ -17,7 +16,7 @@ func createSlash(keeper *keeper.Keeper, ctx sdk.Context, n int) []types.Slash {
 	for i := range items {
 		items[i].ObjectId = sdkmath.NewUint(uint64(i))
 		items[i].Height = uint64(i)
-		items[i].SpOperatorAddress = []byte(fmt.Sprintf("addr-%d", i))
+		items[i].SpId = uint32(i + 1)
 		keeper.SaveSlash(ctx, items[i])
 	}
 	return items
@@ -28,7 +27,7 @@ func TestRecentSlashRemove(t *testing.T) {
 	items := createSlash(keeper, ctx, 10)
 	for _, item := range items {
 		keeper.RemoveSlashUntil(ctx, item.Height)
-		found := keeper.ExistsSlash(ctx, item.SpOperatorAddress, item.ObjectId)
+		found := keeper.ExistsSlash(ctx, item.SpId, item.ObjectId)
 		require.False(t, found)
 	}
 }
diff --git a/x/challenge/types/errors.go b/x/challenge/types/errors.go
index 659432157..b57cf900c 100644
--- a/x/challenge/types/errors.go
+++ b/x/challenge/types/errors.go
@@ -7,7 +7,7 @@ import (
 // x/challenge module sentinel errors
 var (
 	ErrUnknownSp               = errors.Register(ModuleName, 1, "unknown storage provider")
-	ErrUnknownObject           = errors.Register(ModuleName, 2, "unknown object info")
+	ErrUnknownBucketObject     = errors.Register(ModuleName, 2, "unknown bucket info or object info")
 	ErrInvalidSpStatus         = errors.Register(ModuleName, 3, "invalid storage provider status")
 	ErrInvalidObjectStatus     = errors.Register(ModuleName, 4, "invalid object status to challenge")
 	ErrNotStoredOnSp           = errors.Register(ModuleName, 5, "the object is not stored on the storage provider")
@@ -23,4 +23,5 @@ var (
 	ErrNotChallenger           = errors.Register(ModuleName, 15, "not a valid challenger")
 	ErrNotInturnChallenger     = errors.Register(ModuleName, 16, "challenger is not in turn")
 	ErrInvalidParams           = errors.Register(ModuleName, 17, "invalid params")
+	ErrCannotFindGVG           = errors.Register(ModuleName, 18, "fail to find global virtual group for the object")
 )
diff --git a/x/challenge/types/events.pb.go b/x/challenge/types/events.pb.go
index e24e491ae..8b98d8529 100644
--- a/x/challenge/types/events.pb.go
+++ b/x/challenge/types/events.pb.go
@@ -33,13 +33,15 @@ type EventStartChallenge struct {
 	// The segment/piece index of the object info.
 	SegmentIndex uint32 `protobuf:"varint,3,opt,name=segment_index,json=segmentIndex,proto3" json:"segment_index,omitempty"`
 	// The storage provider to be challenged.
-	SpOperatorAddress string `protobuf:"bytes,4,opt,name=sp_operator_address,json=spOperatorAddress,proto3" json:"sp_operator_address,omitempty"`
+	SpId uint32 `protobuf:"varint,4,opt,name=sp_id,json=spId,proto3" json:"sp_id,omitempty"`
+	// The storage provider to be challenged.
+	SpOperatorAddress string `protobuf:"bytes,5,opt,name=sp_operator_address,json=spOperatorAddress,proto3" json:"sp_operator_address,omitempty"`
 	// The redundancy index, which comes from the index of storage providers.
-	RedundancyIndex int32 `protobuf:"varint,5,opt,name=redundancy_index,json=redundancyIndex,proto3" json:"redundancy_index,omitempty"`
+	RedundancyIndex int32 `protobuf:"varint,6,opt,name=redundancy_index,json=redundancyIndex,proto3" json:"redundancy_index,omitempty"`
 	// The challenger who submits the challenge.
-	ChallengerAddress string `protobuf:"bytes,6,opt,name=challenger_address,json=challengerAddress,proto3" json:"challenger_address,omitempty"`
+	ChallengerAddress string `protobuf:"bytes,7,opt,name=challenger_address,json=challengerAddress,proto3" json:"challenger_address,omitempty"`
 	// The challenge will be expired after this height
-	ExpiredHeight uint64 `protobuf:"varint,7,opt,name=expired_height,json=expiredHeight,proto3" json:"expired_height,omitempty"`
+	ExpiredHeight uint64 `protobuf:"varint,8,opt,name=expired_height,json=expiredHeight,proto3" json:"expired_height,omitempty"`
 }
 
 func (m *EventStartChallenge) Reset()         { *m = EventStartChallenge{} }
@@ -89,6 +91,13 @@ func (m *EventStartChallenge) GetSegmentIndex() uint32 {
 	return 0
 }
 
+func (m *EventStartChallenge) GetSpId() uint32 {
+	if m != nil {
+		return m.SpId
+	}
+	return 0
+}
+
 func (m *EventStartChallenge) GetSpOperatorAddress() string {
 	if m != nil {
 		return m.SpOperatorAddress
@@ -124,7 +133,7 @@ type EventAttestChallenge struct {
 	// The result of challenge.
 	Result VoteResult `protobuf:"varint,2,opt,name=result,proto3,enum=greenfield.challenge.VoteResult" json:"result,omitempty"`
 	// The slashed storage provider address.
-	SpOperatorAddress string `protobuf:"bytes,3,opt,name=sp_operator_address,json=spOperatorAddress,proto3" json:"sp_operator_address,omitempty"`
+	SpId uint32 `protobuf:"varint,3,opt,name=sp_id,json=spId,proto3" json:"sp_id,omitempty"`
 	// The slashed amount from the storage provider.
 	SlashAmount string `protobuf:"bytes,4,opt,name=slash_amount,json=slashAmount,proto3" json:"slash_amount,omitempty"`
 	// The address of challenger.
@@ -186,11 +195,11 @@ func (m *EventAttestChallenge) GetResult() VoteResult {
 	return CHALLENGE_FAILED
 }
 
-func (m *EventAttestChallenge) GetSpOperatorAddress() string {
+func (m *EventAttestChallenge) GetSpId() uint32 {
 	if m != nil {
-		return m.SpOperatorAddress
+		return m.SpId
 	}
-	return ""
+	return 0
 }
 
 func (m *EventAttestChallenge) GetSlashAmount() string {
@@ -243,41 +252,41 @@ func init() {
 func init() { proto.RegisterFile("greenfield/challenge/events.proto", fileDescriptor_e9eaa4bfadaa20f8) }
 
 var fileDescriptor_e9eaa4bfadaa20f8 = []byte{
-	// 533 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x53, 0x4f, 0x4f, 0xdb, 0x3e,
-	0x18, 0x6e, 0xa0, 0x14, 0x70, 0x5b, 0x7e, 0x10, 0xfa, 0x1b, 0x19, 0x93, 0x42, 0xca, 0x34, 0xa9,
-	0x3b, 0xb4, 0xd5, 0x36, 0x09, 0x71, 0x2d, 0x13, 0x1a, 0xd5, 0x0e, 0x93, 0x82, 0xb6, 0xc3, 0x2e,
-	0x51, 0x12, 0xbf, 0x4b, 0x3c, 0xa5, 0x76, 0x64, 0xbb, 0xac, 0x7c, 0x8b, 0x9d, 0xf7, 0x39, 0xf8,
-	0x10, 0x1c, 0x11, 0xa7, 0x69, 0x07, 0x34, 0xb5, 0x5f, 0x63, 0x87, 0xa9, 0x8e, 0x9b, 0xb4, 0x13,
-	0x08, 0xf5, 0x56, 0x3f, 0x7f, 0xfc, 0xbc, 0x7d, 0x9c, 0x17, 0x35, 0x23, 0x0e, 0x40, 0xbf, 0x10,
-	0x48, 0x70, 0x37, 0x8c, 0xfd, 0x24, 0x01, 0x1a, 0x41, 0x17, 0x2e, 0x80, 0x4a, 0xd1, 0x49, 0x39,
-	0x93, 0xcc, 0x6c, 0x14, 0x92, 0x4e, 0x2e, 0xd9, 0x7f, 0x1a, 0x32, 0x31, 0x60, 0xc2, 0x53, 0x9a,
-	0x6e, 0x76, 0xc8, 0x0c, 0xfb, 0x8d, 0x88, 0x45, 0x2c, 0xc3, 0xa7, 0xbf, 0x34, 0xea, 0xdc, 0x9b,
-	0x24, 0x2f, 0x53, 0xd0, 0xbe, 0xc3, 0x3f, 0x2b, 0x68, 0xf7, 0x74, 0x9a, 0x7c, 0x2e, 0x7d, 0x2e,
-	0xdf, 0xce, 0x34, 0x66, 0x13, 0xd5, 0x72, 0x83, 0x47, 0xb0, 0x65, 0x38, 0x46, 0xab, 0xec, 0x56,
-	0x73, 0xac, 0x8f, 0xcd, 0x63, 0xb4, 0xc9, 0x82, 0xaf, 0x10, 0xca, 0x29, 0xbf, 0xe2, 0x18, 0xad,
-	0xcd, 0x93, 0x67, 0xd7, 0x77, 0x07, 0xa5, 0x5f, 0x77, 0x07, 0xe5, 0x8f, 0x84, 0xca, 0xdb, 0xab,
-	0x76, 0x55, 0xcf, 0x38, 0x3d, 0xba, 0x1b, 0x99, 0xba, 0x8f, 0xcd, 0xe7, 0xa8, 0x2e, 0x20, 0x1a,
-	0x00, 0x95, 0x1e, 0xa1, 0x18, 0x46, 0xd6, 0xaa, 0x63, 0xb4, 0xea, 0x6e, 0x4d, 0x83, 0xfd, 0x29,
-	0x66, 0x9e, 0xa1, 0x5d, 0x91, 0x7a, 0x2c, 0x05, 0xee, 0x4b, 0xc6, 0x3d, 0x1f, 0x63, 0x0e, 0x42,
-	0x58, 0x65, 0x15, 0x64, 0xdd, 0x5e, 0xb5, 0x1b, 0xfa, 0xf2, 0x5e, 0xc6, 0x9c, 0x4b, 0x4e, 0x68,
-	0xe4, 0xee, 0x88, 0xf4, 0x83, 0xf6, 0x68, 0xc2, 0x7c, 0x89, 0xb6, 0x39, 0xe0, 0x21, 0xc5, 0x3e,
-	0x0d, 0x2f, 0x75, 0xe2, 0x9a, 0x63, 0xb4, 0xd6, 0xdc, 0xff, 0x0a, 0x3c, 0x0b, 0x7d, 0x87, 0xcc,
-	0xfc, 0x2f, 0x16, 0x99, 0x95, 0xc7, 0x32, 0x0b, 0xcf, 0x2c, 0xf3, 0x05, 0xda, 0x82, 0x51, 0x4a,
-	0x38, 0x60, 0x2f, 0x06, 0x12, 0xc5, 0xd2, 0x5a, 0x57, 0x0d, 0xd6, 0x35, 0x7a, 0xa6, 0xc0, 0xc3,
-	0x1f, 0x65, 0xd4, 0x50, 0xf5, 0xf7, 0xa4, 0x04, 0xb1, 0x6c, 0xff, 0x15, 0x0e, 0x62, 0x98, 0x48,
-	0x55, 0xfe, 0xd6, 0x6b, 0xa7, 0x73, 0xdf, 0x47, 0xd3, 0xf9, 0xc4, 0x24, 0xb8, 0x4a, 0xe7, 0x6a,
-	0xfd, 0x43, 0xd5, 0xae, 0x2e, 0x5f, 0x6d, 0x13, 0xd5, 0x44, 0xe2, 0x8b, 0xd8, 0xf3, 0x07, 0x6c,
-	0x48, 0x65, 0xf6, 0x3a, 0x6e, 0x55, 0x61, 0x3d, 0x05, 0x3d, 0x50, 0xe9, 0xda, 0xf2, 0x95, 0x1e,
-	0x23, 0x6b, 0xee, 0x22, 0x0e, 0xdf, 0x7c, 0x8e, 0x67, 0xb9, 0xea, 0x85, 0xdc, 0x27, 0x05, 0xef,
-	0x2a, 0x5a, 0x8f, 0x70, 0x8a, 0x76, 0xc4, 0x30, 0x18, 0x10, 0x29, 0xe7, 0x26, 0x58, 0x7f, 0x64,
-	0x82, 0xed, 0xdc, 0x32, 0x1b, 0xe0, 0x08, 0xed, 0x15, 0xd7, 0x2c, 0xe6, 0x6f, 0xa8, 0xfc, 0xff,
-	0x73, 0x7a, 0x21, 0xfe, 0x08, 0xed, 0x5d, 0xf8, 0x09, 0xc1, 0xaa, 0xec, 0x45, 0x1f, 0xca, 0x7c,
-	0x39, 0x3d, 0xef, 0x3b, 0x79, 0x7f, 0x3d, 0xb6, 0x8d, 0x9b, 0xb1, 0x6d, 0xfc, 0x1e, 0xdb, 0xc6,
-	0xf7, 0x89, 0x5d, 0xba, 0x99, 0xd8, 0xa5, 0x9f, 0x13, 0xbb, 0xf4, 0xf9, 0x55, 0x44, 0x64, 0x3c,
-	0x0c, 0x3a, 0x21, 0x1b, 0x74, 0x03, 0x1a, 0xb4, 0xc3, 0xd8, 0x27, 0xb4, 0x3b, 0xb7, 0xec, 0xa3,
-	0x7f, 0xd7, 0x3d, 0xa8, 0xa8, 0x7d, 0x7f, 0xf3, 0x37, 0x00, 0x00, 0xff, 0xff, 0x31, 0xa0, 0x83,
-	0x4c, 0x7d, 0x04, 0x00, 0x00,
+	// 542 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x53, 0xcb, 0x6e, 0xd3, 0x4c,
+	0x14, 0x8e, 0xff, 0x5c, 0xfe, 0x66, 0x92, 0x94, 0xd6, 0x09, 0xd4, 0x14, 0xc9, 0x75, 0x8a, 0x90,
+	0xc2, 0x22, 0x89, 0x00, 0xa9, 0xea, 0x36, 0x45, 0x15, 0x8d, 0x58, 0x20, 0xb9, 0x82, 0x05, 0x1b,
+	0xcb, 0xf6, 0x1c, 0x6c, 0x23, 0x67, 0xc6, 0x9a, 0x99, 0x94, 0xf4, 0x2d, 0xe0, 0x5d, 0xfa, 0x10,
+	0x5d, 0x56, 0x5d, 0x21, 0x16, 0x15, 0x4a, 0xc4, 0x7b, 0x20, 0x8f, 0x27, 0x76, 0x82, 0x8a, 0x50,
+	0x76, 0x9e, 0xef, 0x32, 0xdf, 0xf1, 0x39, 0x73, 0x50, 0x37, 0x60, 0x00, 0xe4, 0x53, 0x04, 0x31,
+	0x1e, 0xfa, 0xa1, 0x1b, 0xc7, 0x40, 0x02, 0x18, 0xc2, 0x05, 0x10, 0xc1, 0x07, 0x09, 0xa3, 0x82,
+	0xea, 0x9d, 0x42, 0x32, 0xc8, 0x25, 0xfb, 0x8f, 0x7d, 0xca, 0x27, 0x94, 0x3b, 0x52, 0x33, 0xcc,
+	0x0e, 0x99, 0x61, 0xbf, 0x13, 0xd0, 0x80, 0x66, 0x78, 0xfa, 0xa5, 0x50, 0xeb, 0xde, 0x24, 0x71,
+	0x99, 0x80, 0xf2, 0x1d, 0x7e, 0x2b, 0xa3, 0xf6, 0x69, 0x9a, 0x7c, 0x2e, 0x5c, 0x26, 0x5e, 0x2f,
+	0x35, 0x7a, 0x17, 0x35, 0x73, 0x83, 0x13, 0x61, 0x43, 0xb3, 0xb4, 0x5e, 0xc5, 0x6e, 0xe4, 0xd8,
+	0x18, 0xeb, 0xc7, 0xa8, 0x4e, 0xbd, 0xcf, 0xe0, 0x8b, 0x94, 0xff, 0xcf, 0xd2, 0x7a, 0xf5, 0x93,
+	0x27, 0xd7, 0x77, 0x07, 0xa5, 0x1f, 0x77, 0x07, 0x95, 0xf7, 0x11, 0x11, 0xb7, 0x57, 0xfd, 0x86,
+	0xaa, 0x31, 0x3d, 0xda, 0x5b, 0x99, 0x7a, 0x8c, 0xf5, 0xa7, 0xa8, 0xc5, 0x21, 0x98, 0x00, 0x11,
+	0x4e, 0x44, 0x30, 0xcc, 0x8c, 0xb2, 0xa5, 0xf5, 0x5a, 0x76, 0x53, 0x81, 0xe3, 0x14, 0xd3, 0xdb,
+	0xa8, 0xca, 0x93, 0xf4, 0xea, 0x8a, 0x24, 0x2b, 0x3c, 0x19, 0x63, 0xfd, 0x0c, 0xb5, 0x79, 0xe2,
+	0xd0, 0x04, 0x98, 0x2b, 0x28, 0x73, 0x5c, 0x8c, 0x19, 0x70, 0x6e, 0x54, 0x65, 0xba, 0x71, 0x7b,
+	0xd5, 0xef, 0xa8, 0xc4, 0x51, 0xc6, 0x9c, 0x0b, 0x16, 0x91, 0xc0, 0xde, 0xe5, 0xc9, 0x3b, 0xe5,
+	0x51, 0x84, 0xfe, 0x1c, 0xed, 0x30, 0xc0, 0x53, 0x82, 0x5d, 0xe2, 0x5f, 0xaa, 0x32, 0x6a, 0x96,
+	0xd6, 0xab, 0xda, 0x0f, 0x0a, 0x3c, 0xab, 0xe4, 0x0d, 0xd2, 0xf3, 0xff, 0x2e, 0x32, 0xff, 0xff,
+	0x57, 0x66, 0xe1, 0x59, 0x66, 0x3e, 0x43, 0xdb, 0x30, 0x4b, 0x22, 0x06, 0xd8, 0x09, 0x21, 0x0a,
+	0x42, 0x61, 0x6c, 0xc9, 0xb6, 0xb6, 0x14, 0x7a, 0x26, 0xc1, 0xc3, 0x5f, 0x65, 0xd4, 0x91, 0x33,
+	0x19, 0x09, 0x01, 0x7c, 0xd3, 0xa1, 0xd4, 0x18, 0xf0, 0x69, 0x2c, 0xe4, 0x44, 0xb6, 0x5f, 0x5a,
+	0x83, 0xfb, 0x5e, 0xd2, 0xe0, 0x03, 0x15, 0x60, 0x4b, 0x9d, 0xad, 0xf4, 0x45, 0xbf, 0xcb, 0x2b,
+	0xfd, 0xee, 0xa2, 0x26, 0x8f, 0x5d, 0x1e, 0x3a, 0xee, 0x84, 0x4e, 0x89, 0x90, 0xb3, 0xa8, 0xdb,
+	0x0d, 0x89, 0x8d, 0x24, 0xf4, 0x97, 0xee, 0x54, 0x37, 0xef, 0xce, 0x31, 0x32, 0x56, 0x2e, 0x62,
+	0xf0, 0xc5, 0x65, 0x78, 0x99, 0x5b, 0x93, 0xb9, 0x8f, 0x0a, 0xde, 0x96, 0xb4, 0x2a, 0xe1, 0x14,
+	0xed, 0xf2, 0xa9, 0x37, 0x89, 0x84, 0xd8, 0x60, 0x3e, 0x3b, 0xb9, 0x65, 0x59, 0xc0, 0x11, 0xda,
+	0x2b, 0xae, 0x59, 0xcf, 0xdf, 0x92, 0xf9, 0x0f, 0x73, 0x7a, 0x2d, 0xfe, 0x08, 0xed, 0x5d, 0xb8,
+	0x71, 0x84, 0xe5, 0x93, 0x5c, 0xf7, 0xa1, 0xcc, 0x97, 0xd3, 0xab, 0xbe, 0x93, 0xb7, 0xd7, 0x73,
+	0x53, 0xbb, 0x99, 0x9b, 0xda, 0xcf, 0xb9, 0xa9, 0x7d, 0x5d, 0x98, 0xa5, 0x9b, 0x85, 0x59, 0xfa,
+	0xbe, 0x30, 0x4b, 0x1f, 0x5f, 0x04, 0x91, 0x08, 0xa7, 0xde, 0xc0, 0xa7, 0x93, 0xa1, 0x47, 0xbc,
+	0xbe, 0x1f, 0xba, 0x11, 0x19, 0xae, 0x2c, 0xf3, 0xec, 0xcf, 0x75, 0xf6, 0x6a, 0x72, 0x9f, 0x5f,
+	0xfd, 0x0e, 0x00, 0x00, 0xff, 0xff, 0x86, 0xd6, 0xb3, 0x28, 0x5d, 0x04, 0x00, 0x00,
 }
 
 func (m *EventStartChallenge) Marshal() (dAtA []byte, err error) {
@@ -303,26 +312,31 @@ func (m *EventStartChallenge) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	if m.ExpiredHeight != 0 {
 		i = encodeVarintEvents(dAtA, i, uint64(m.ExpiredHeight))
 		i--
-		dAtA[i] = 0x38
+		dAtA[i] = 0x40
 	}
 	if len(m.ChallengerAddress) > 0 {
 		i -= len(m.ChallengerAddress)
 		copy(dAtA[i:], m.ChallengerAddress)
 		i = encodeVarintEvents(dAtA, i, uint64(len(m.ChallengerAddress)))
 		i--
-		dAtA[i] = 0x32
+		dAtA[i] = 0x3a
 	}
 	if m.RedundancyIndex != 0 {
 		i = encodeVarintEvents(dAtA, i, uint64(m.RedundancyIndex))
 		i--
-		dAtA[i] = 0x28
+		dAtA[i] = 0x30
 	}
 	if len(m.SpOperatorAddress) > 0 {
 		i -= len(m.SpOperatorAddress)
 		copy(dAtA[i:], m.SpOperatorAddress)
 		i = encodeVarintEvents(dAtA, i, uint64(len(m.SpOperatorAddress)))
 		i--
-		dAtA[i] = 0x22
+		dAtA[i] = 0x2a
+	}
+	if m.SpId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.SpId))
+		i--
+		dAtA[i] = 0x20
 	}
 	if m.SegmentIndex != 0 {
 		i = encodeVarintEvents(dAtA, i, uint64(m.SegmentIndex))
@@ -409,12 +423,10 @@ func (m *EventAttestChallenge) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		i--
 		dAtA[i] = 0x22
 	}
-	if len(m.SpOperatorAddress) > 0 {
-		i -= len(m.SpOperatorAddress)
-		copy(dAtA[i:], m.SpOperatorAddress)
-		i = encodeVarintEvents(dAtA, i, uint64(len(m.SpOperatorAddress)))
+	if m.SpId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.SpId))
 		i--
-		dAtA[i] = 0x1a
+		dAtA[i] = 0x18
 	}
 	if m.Result != 0 {
 		i = encodeVarintEvents(dAtA, i, uint64(m.Result))
@@ -454,6 +466,9 @@ func (m *EventStartChallenge) Size() (n int) {
 	if m.SegmentIndex != 0 {
 		n += 1 + sovEvents(uint64(m.SegmentIndex))
 	}
+	if m.SpId != 0 {
+		n += 1 + sovEvents(uint64(m.SpId))
+	}
 	l = len(m.SpOperatorAddress)
 	if l > 0 {
 		n += 1 + l + sovEvents(uint64(l))
@@ -483,9 +498,8 @@ func (m *EventAttestChallenge) Size() (n int) {
 	if m.Result != 0 {
 		n += 1 + sovEvents(uint64(m.Result))
 	}
-	l = len(m.SpOperatorAddress)
-	if l > 0 {
-		n += 1 + l + sovEvents(uint64(l))
+	if m.SpId != 0 {
+		n += 1 + sovEvents(uint64(m.SpId))
 	}
 	l = len(m.SlashAmount)
 	if l > 0 {
@@ -622,6 +636,25 @@ func (m *EventStartChallenge) Unmarshal(dAtA []byte) error {
 				}
 			}
 		case 4:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field SpId", wireType)
+			}
+			m.SpId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.SpId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 5:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field SpOperatorAddress", wireType)
 			}
@@ -653,7 +686,7 @@ func (m *EventStartChallenge) Unmarshal(dAtA []byte) error {
 			}
 			m.SpOperatorAddress = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
-		case 5:
+		case 6:
 			if wireType != 0 {
 				return fmt.Errorf("proto: wrong wireType = %d for field RedundancyIndex", wireType)
 			}
@@ -672,7 +705,7 @@ func (m *EventStartChallenge) Unmarshal(dAtA []byte) error {
 					break
 				}
 			}
-		case 6:
+		case 7:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field ChallengerAddress", wireType)
 			}
@@ -704,7 +737,7 @@ func (m *EventStartChallenge) Unmarshal(dAtA []byte) error {
 			}
 			m.ChallengerAddress = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
-		case 7:
+		case 8:
 			if wireType != 0 {
 				return fmt.Errorf("proto: wrong wireType = %d for field ExpiredHeight", wireType)
 			}
@@ -812,10 +845,10 @@ func (m *EventAttestChallenge) Unmarshal(dAtA []byte) error {
 				}
 			}
 		case 3:
-			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field SpOperatorAddress", wireType)
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field SpId", wireType)
 			}
-			var stringLen uint64
+			m.SpId = 0
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowEvents
@@ -825,24 +858,11 @@ func (m *EventAttestChallenge) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				stringLen |= uint64(b&0x7F) << shift
+				m.SpId |= uint32(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
-			intStringLen := int(stringLen)
-			if intStringLen < 0 {
-				return ErrInvalidLengthEvents
-			}
-			postIndex := iNdEx + intStringLen
-			if postIndex < 0 {
-				return ErrInvalidLengthEvents
-			}
-			if postIndex > l {
-				return io.ErrUnexpectedEOF
-			}
-			m.SpOperatorAddress = string(dAtA[iNdEx:postIndex])
-			iNdEx = postIndex
 		case 4:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field SlashAmount", wireType)
diff --git a/x/challenge/types/expected_keepers.go b/x/challenge/types/expected_keepers.go
index e8e2d6b81..2781ae473 100644
--- a/x/challenge/types/expected_keepers.go
+++ b/x/challenge/types/expected_keepers.go
@@ -8,12 +8,14 @@ import (
 
 	sp "github.com/bnb-chain/greenfield/x/sp/types"
 	storage "github.com/bnb-chain/greenfield/x/storage/types"
+	"github.com/bnb-chain/greenfield/x/virtualgroup/types"
 )
 
 type SpKeeper interface {
-	GetStorageProvider(ctx sdk.Context, addr sdk.AccAddress) (sp *sp.StorageProvider, found bool)
+	GetStorageProvider(ctx sdk.Context, id uint32) (*sp.StorageProvider, bool)
+	GetStorageProviderByOperatorAddr(ctx sdk.Context, opAddr sdk.AccAddress) (sp *sp.StorageProvider, found bool)
 	DepositDenomForSP(ctx sdk.Context) (res string)
-	Slash(ctx sdk.Context, spAcc sdk.AccAddress, rewardInfos []sp.RewardInfo) error
+	Slash(ctx sdk.Context, spID uint32, rewardInfos []sp.RewardInfo) error
 }
 
 type StakingKeeper interface {
@@ -27,6 +29,7 @@ type StorageKeeper interface {
 	GetObjectInfoCount(ctx sdk.Context) sdkmath.Uint
 	GetBucketInfo(ctx sdk.Context, bucketName string) (*storage.BucketInfo, bool)
 	MaxSegmentSize(ctx sdk.Context) (res uint64)
+	GetObjectGVG(ctx sdk.Context, bucketID sdkmath.Uint, lvgID uint32) (*types.GlobalVirtualGroup, bool)
 }
 
 type PaymentKeeper interface {
diff --git a/x/challenge/types/expected_keepers_mocks.go b/x/challenge/types/expected_keepers_mocks.go
index c1acc967b..dd9db9fba 100644
--- a/x/challenge/types/expected_keepers_mocks.go
+++ b/x/challenge/types/expected_keepers_mocks.go
@@ -1,5 +1,5 @@
 // Code generated by MockGen. DO NOT EDIT.
-// Source: expected_keepers.go
+// Source: x/challenge/types/expected_keepers.go
 
 // Package types is a generated GoMock package.
 package types
@@ -8,351 +8,381 @@ import (
 	reflect "reflect"
 
 	math "cosmossdk.io/math"
-	types1 "github.com/cosmos/cosmos-sdk/types"
-	types2 "github.com/cosmos/cosmos-sdk/x/auth/types"
-	types3 "github.com/cosmos/cosmos-sdk/x/staking/types"
-	gomock "github.com/golang/mock/gomock"
-
 	types "github.com/bnb-chain/greenfield/x/sp/types"
 	types0 "github.com/bnb-chain/greenfield/x/storage/types"
+	types1 "github.com/bnb-chain/greenfield/x/virtualgroup/types"
+	types2 "github.com/cosmos/cosmos-sdk/types"
+	types3 "github.com/cosmos/cosmos-sdk/x/auth/types"
+	types4 "github.com/cosmos/cosmos-sdk/x/staking/types"
+	gomock "github.com/golang/mock/gomock"
 )
 
-// MockAccountKeeper is a mock of AccountKeeper interface
-type MockAccountKeeper struct {
-	ctrl     *gomock.Controller
-	recorder *MockAccountKeeperMockRecorder
-}
-
-// MockAccountKeeperMockRecorder is the mock recorder for MockAccountKeeper
-type MockAccountKeeperMockRecorder struct {
-	mock *MockAccountKeeper
-}
-
-// NewMockAccountKeeper creates a new mock instance
-func NewMockAccountKeeper(ctrl *gomock.Controller) *MockAccountKeeper {
-	mock := &MockAccountKeeper{ctrl: ctrl}
-	mock.recorder = &MockAccountKeeperMockRecorder{mock}
-	return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use
-func (m *MockAccountKeeper) EXPECT() *MockAccountKeeperMockRecorder {
-	return m.recorder
-}
-
-// GetAccount mocks base method
-func (m *MockAccountKeeper) GetAccount(arg0 types1.Context, arg1 types1.AccAddress) types2.AccountI {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetAccount", arg0, arg1)
-	ret0, _ := ret[0].(types2.AccountI)
-	return ret0
-}
-
-// GetAccount indicates an expected call of GetAccount
-func (mr *MockAccountKeeperMockRecorder) GetAccount(arg0, arg1 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetAccount), arg0, arg1)
-}
-
-// MockBankKeeper is a mock of BankKeeper interface
-type MockBankKeeper struct {
-	ctrl     *gomock.Controller
-	recorder *MockBankKeeperMockRecorder
-}
-
-// MockBankKeeperMockRecorder is the mock recorder for MockBankKeeper
-type MockBankKeeperMockRecorder struct {
-	mock *MockBankKeeper
-}
-
-// NewMockBankKeeper creates a new mock instance
-func NewMockBankKeeper(ctrl *gomock.Controller) *MockBankKeeper {
-	mock := &MockBankKeeper{ctrl: ctrl}
-	mock.recorder = &MockBankKeeperMockRecorder{mock}
-	return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use
-func (m *MockBankKeeper) EXPECT() *MockBankKeeperMockRecorder {
-	return m.recorder
-}
-
-// SpendableCoins mocks base method
-func (m *MockBankKeeper) SpendableCoins(arg0 types1.Context, arg1 types1.AccAddress) types1.Coins {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "SpendableCoins", arg0, arg1)
-	ret0, _ := ret[0].(types1.Coins)
-	return ret0
-}
-
-// SpendableCoins indicates an expected call of SpendableCoins
-func (mr *MockBankKeeperMockRecorder) SpendableCoins(arg0, arg1 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpendableCoins", reflect.TypeOf((*MockBankKeeper)(nil).SpendableCoins), arg0, arg1)
-}
-
-// MockSpKeeper is a mock of SpKeeper interface
+// MockSpKeeper is a mock of SpKeeper interface.
 type MockSpKeeper struct {
 	ctrl     *gomock.Controller
 	recorder *MockSpKeeperMockRecorder
 }
 
-// MockSpKeeperMockRecorder is the mock recorder for MockSpKeeper
+// MockSpKeeperMockRecorder is the mock recorder for MockSpKeeper.
 type MockSpKeeperMockRecorder struct {
 	mock *MockSpKeeper
 }
 
-// NewMockSpKeeper creates a new mock instance
+// NewMockSpKeeper creates a new mock instance.
 func NewMockSpKeeper(ctrl *gomock.Controller) *MockSpKeeper {
 	mock := &MockSpKeeper{ctrl: ctrl}
 	mock.recorder = &MockSpKeeperMockRecorder{mock}
 	return mock
 }
 
-// EXPECT returns an object that allows the caller to indicate expected use
+// EXPECT returns an object that allows the caller to indicate expected use.
 func (m *MockSpKeeper) EXPECT() *MockSpKeeperMockRecorder {
 	return m.recorder
 }
 
-// DepositDenomForSP mocks base method
-func (m *MockSpKeeper) DepositDenomForSP(arg0 types1.Context) string {
+// DepositDenomForSP mocks base method.
+func (m *MockSpKeeper) DepositDenomForSP(ctx types2.Context) string {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "DepositDenomForSP", arg0)
+	ret := m.ctrl.Call(m, "DepositDenomForSP", ctx)
 	ret0, _ := ret[0].(string)
 	return ret0
 }
 
-// DepositDenomForSP indicates an expected call of DepositDenomForSP
-func (mr *MockSpKeeperMockRecorder) DepositDenomForSP(arg0 interface{}) *gomock.Call {
+// DepositDenomForSP indicates an expected call of DepositDenomForSP.
+func (mr *MockSpKeeperMockRecorder) DepositDenomForSP(ctx interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DepositDenomForSP", reflect.TypeOf((*MockSpKeeper)(nil).DepositDenomForSP), arg0)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DepositDenomForSP", reflect.TypeOf((*MockSpKeeper)(nil).DepositDenomForSP), ctx)
 }
 
-// GetStorageProvider mocks base method
-func (m *MockSpKeeper) GetStorageProvider(arg0 types1.Context, arg1 types1.AccAddress) (*types.StorageProvider, bool) {
+// GetStorageProvider mocks base method.
+func (m *MockSpKeeper) GetStorageProvider(ctx types2.Context, id uint32) (*types.StorageProvider, bool) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetStorageProvider", arg0, arg1)
+	ret := m.ctrl.Call(m, "GetStorageProvider", ctx, id)
 	ret0, _ := ret[0].(*types.StorageProvider)
 	ret1, _ := ret[1].(bool)
 	return ret0, ret1
 }
 
-// GetStorageProvider indicates an expected call of GetStorageProvider
-func (mr *MockSpKeeperMockRecorder) GetStorageProvider(arg0, arg1 interface{}) *gomock.Call {
+// GetStorageProvider indicates an expected call of GetStorageProvider.
+func (mr *MockSpKeeperMockRecorder) GetStorageProvider(ctx, id interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageProvider", reflect.TypeOf((*MockSpKeeper)(nil).GetStorageProvider), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageProvider", reflect.TypeOf((*MockSpKeeper)(nil).GetStorageProvider), ctx, id)
 }
 
-// Slash mocks base method
-func (m *MockSpKeeper) Slash(arg0 types1.Context, arg1 types1.AccAddress, arg2 []types.RewardInfo) error {
+// GetStorageProviderByOperatorAddr mocks base method.
+func (m *MockSpKeeper) GetStorageProviderByOperatorAddr(ctx types2.Context, opAddr types2.AccAddress) (*types.StorageProvider, bool) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "Slash", arg0, arg1, arg2)
-	ret0, _ := ret[0].(error)
-	return ret0
-}
-
-// Slash indicates an expected call of Slash
-func (mr *MockSpKeeperMockRecorder) Slash(arg0, arg1, arg2 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Slash", reflect.TypeOf((*MockSpKeeper)(nil).Slash), arg0, arg1, arg2)
-}
-
-// MockPaymentKeeper is a mock of PaymentKeeper interface
-type MockPaymentKeeper struct {
-	ctrl     *gomock.Controller
-	recorder *MockPaymentKeeperMockRecorder
-}
-
-// MockPaymentKeeperMockRecorder is the mock recorder for MockPaymentKeeper
-type MockPaymentKeeperMockRecorder struct {
-	mock *MockPaymentKeeper
-}
-
-// NewMockPaymentKeeper creates a new mock instance
-func NewMockPaymentKeeper(ctrl *gomock.Controller) *MockPaymentKeeper {
-	mock := &MockPaymentKeeper{ctrl: ctrl}
-	mock.recorder = &MockPaymentKeeperMockRecorder{mock}
-	return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use
-func (m *MockPaymentKeeper) EXPECT() *MockPaymentKeeperMockRecorder {
-	return m.recorder
-}
-
-// QueryDynamicBalance mocks base method
-func (m *MockPaymentKeeper) QueryDynamicBalance(arg0 types1.Context, arg1 types1.AccAddress) (math.Int, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "QueryDynamicBalance", arg0, arg1)
-	ret0, _ := ret[0].(math.Int)
-	ret1, _ := ret[1].(error)
+	ret := m.ctrl.Call(m, "GetStorageProviderByOperatorAddr", ctx, opAddr)
+	ret0, _ := ret[0].(*types.StorageProvider)
+	ret1, _ := ret[1].(bool)
 	return ret0, ret1
 }
 
-// QueryDynamicBalance indicates an expected call of QueryDynamicBalance
-func (mr *MockPaymentKeeperMockRecorder) QueryDynamicBalance(arg0, arg1 interface{}) *gomock.Call {
+// GetStorageProviderByOperatorAddr indicates an expected call of GetStorageProviderByOperatorAddr.
+func (mr *MockSpKeeperMockRecorder) GetStorageProviderByOperatorAddr(ctx, opAddr interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryDynamicBalance", reflect.TypeOf((*MockPaymentKeeper)(nil).QueryDynamicBalance), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageProviderByOperatorAddr", reflect.TypeOf((*MockSpKeeper)(nil).GetStorageProviderByOperatorAddr), ctx, opAddr)
 }
 
-// Withdraw mocks base method
-func (m *MockPaymentKeeper) Withdraw(arg0 types1.Context, arg1, arg2 types1.AccAddress, arg3 math.Int) error {
+// Slash mocks base method.
+func (m *MockSpKeeper) Slash(ctx types2.Context, spID uint32, rewardInfos []types.RewardInfo) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "Withdraw", arg0, arg1, arg2, arg3)
+	ret := m.ctrl.Call(m, "Slash", ctx, spID, rewardInfos)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
-// Withdraw indicates an expected call of Withdraw
-func (mr *MockPaymentKeeperMockRecorder) Withdraw(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
+// Slash indicates an expected call of Slash.
+func (mr *MockSpKeeperMockRecorder) Slash(ctx, spID, rewardInfos interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Withdraw", reflect.TypeOf((*MockPaymentKeeper)(nil).Withdraw), arg0, arg1, arg2, arg3)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Slash", reflect.TypeOf((*MockSpKeeper)(nil).Slash), ctx, spID, rewardInfos)
 }
 
-// MockStakingKeeper is a mock of StakingKeeper interface
+// MockStakingKeeper is a mock of StakingKeeper interface.
 type MockStakingKeeper struct {
 	ctrl     *gomock.Controller
 	recorder *MockStakingKeeperMockRecorder
 }
 
-// MockStakingKeeperMockRecorder is the mock recorder for MockStakingKeeper
+// MockStakingKeeperMockRecorder is the mock recorder for MockStakingKeeper.
 type MockStakingKeeperMockRecorder struct {
 	mock *MockStakingKeeper
 }
 
-// NewMockStakingKeeper creates a new mock instance
+// NewMockStakingKeeper creates a new mock instance.
 func NewMockStakingKeeper(ctrl *gomock.Controller) *MockStakingKeeper {
 	mock := &MockStakingKeeper{ctrl: ctrl}
 	mock.recorder = &MockStakingKeeperMockRecorder{mock}
 	return mock
 }
 
-// EXPECT returns an object that allows the caller to indicate expected use
+// EXPECT returns an object that allows the caller to indicate expected use.
 func (m *MockStakingKeeper) EXPECT() *MockStakingKeeperMockRecorder {
 	return m.recorder
 }
 
-// GetHistoricalInfo mocks base method
-func (m *MockStakingKeeper) GetHistoricalInfo(arg0 types1.Context, arg1 int64) (types3.HistoricalInfo, bool) {
+// GetHistoricalInfo mocks base method.
+func (m *MockStakingKeeper) GetHistoricalInfo(ctx types2.Context, height int64) (types4.HistoricalInfo, bool) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetHistoricalInfo", arg0, arg1)
-	ret0, _ := ret[0].(types3.HistoricalInfo)
+	ret := m.ctrl.Call(m, "GetHistoricalInfo", ctx, height)
+	ret0, _ := ret[0].(types4.HistoricalInfo)
 	ret1, _ := ret[1].(bool)
 	return ret0, ret1
 }
 
-// GetHistoricalInfo indicates an expected call of GetHistoricalInfo
-func (mr *MockStakingKeeperMockRecorder) GetHistoricalInfo(arg0, arg1 interface{}) *gomock.Call {
+// GetHistoricalInfo indicates an expected call of GetHistoricalInfo.
+func (mr *MockStakingKeeperMockRecorder) GetHistoricalInfo(ctx, height interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHistoricalInfo", reflect.TypeOf((*MockStakingKeeper)(nil).GetHistoricalInfo), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHistoricalInfo", reflect.TypeOf((*MockStakingKeeper)(nil).GetHistoricalInfo), ctx, height)
 }
 
-// GetLastValidators mocks base method
-func (m *MockStakingKeeper) GetLastValidators(arg0 types1.Context) []types3.Validator {
+// GetLastValidators mocks base method.
+func (m *MockStakingKeeper) GetLastValidators(ctx types2.Context) []types4.Validator {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetLastValidators", arg0)
-	ret0, _ := ret[0].([]types3.Validator)
+	ret := m.ctrl.Call(m, "GetLastValidators", ctx)
+	ret0, _ := ret[0].([]types4.Validator)
 	return ret0
 }
 
-// GetLastValidators indicates an expected call of GetLastValidators
-func (mr *MockStakingKeeperMockRecorder) GetLastValidators(arg0 interface{}) *gomock.Call {
+// GetLastValidators indicates an expected call of GetLastValidators.
+func (mr *MockStakingKeeperMockRecorder) GetLastValidators(ctx interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLastValidators", reflect.TypeOf((*MockStakingKeeper)(nil).GetLastValidators), arg0)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLastValidators", reflect.TypeOf((*MockStakingKeeper)(nil).GetLastValidators), ctx)
 }
 
-// MockStorageKeeper is a mock of StorageKeeper interface
+// MockStorageKeeper is a mock of StorageKeeper interface.
 type MockStorageKeeper struct {
 	ctrl     *gomock.Controller
 	recorder *MockStorageKeeperMockRecorder
 }
 
-// MockStorageKeeperMockRecorder is the mock recorder for MockStorageKeeper
+// MockStorageKeeperMockRecorder is the mock recorder for MockStorageKeeper.
 type MockStorageKeeperMockRecorder struct {
 	mock *MockStorageKeeper
 }
 
-// NewMockStorageKeeper creates a new mock instance
+// NewMockStorageKeeper creates a new mock instance.
 func NewMockStorageKeeper(ctrl *gomock.Controller) *MockStorageKeeper {
 	mock := &MockStorageKeeper{ctrl: ctrl}
 	mock.recorder = &MockStorageKeeperMockRecorder{mock}
 	return mock
 }
 
-// EXPECT returns an object that allows the caller to indicate expected use
+// EXPECT returns an object that allows the caller to indicate expected use.
 func (m *MockStorageKeeper) EXPECT() *MockStorageKeeperMockRecorder {
 	return m.recorder
 }
 
-// GetBucketInfo mocks base method
-func (m *MockStorageKeeper) GetBucketInfo(arg0 types1.Context, arg1 string) (*types0.BucketInfo, bool) {
+// GetBucketInfo mocks base method.
+func (m *MockStorageKeeper) GetBucketInfo(ctx types2.Context, bucketName string) (*types0.BucketInfo, bool) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetBucketInfo", arg0, arg1)
+	ret := m.ctrl.Call(m, "GetBucketInfo", ctx, bucketName)
 	ret0, _ := ret[0].(*types0.BucketInfo)
 	ret1, _ := ret[1].(bool)
 	return ret0, ret1
 }
 
-// GetBucketInfo indicates an expected call of GetBucketInfo
-func (mr *MockStorageKeeperMockRecorder) GetBucketInfo(arg0, arg1 interface{}) *gomock.Call {
+// GetBucketInfo indicates an expected call of GetBucketInfo.
+func (mr *MockStorageKeeperMockRecorder) GetBucketInfo(ctx, bucketName interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBucketInfo", reflect.TypeOf((*MockStorageKeeper)(nil).GetBucketInfo), ctx, bucketName)
+}
+
+// GetObjectGVG mocks base method.
+func (m *MockStorageKeeper) GetObjectGVG(ctx types2.Context, bucketID math.Uint, lvgID uint32) (*types1.GlobalVirtualGroup, bool) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetObjectGVG", ctx, bucketID, lvgID)
+	ret0, _ := ret[0].(*types1.GlobalVirtualGroup)
+	ret1, _ := ret[1].(bool)
+	return ret0, ret1
+}
+
+// GetObjectGVG indicates an expected call of GetObjectGVG.
+func (mr *MockStorageKeeperMockRecorder) GetObjectGVG(ctx, bucketID, lvgID interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBucketInfo", reflect.TypeOf((*MockStorageKeeper)(nil).GetBucketInfo), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetObjectGVG", reflect.TypeOf((*MockStorageKeeper)(nil).GetObjectGVG), ctx, bucketID, lvgID)
 }
 
-// GetObjectInfo mocks base method
-func (m *MockStorageKeeper) GetObjectInfo(arg0 types1.Context, arg1, arg2 string) (*types0.ObjectInfo, bool) {
+// GetObjectInfo mocks base method.
+func (m *MockStorageKeeper) GetObjectInfo(ctx types2.Context, bucketName, objectName string) (*types0.ObjectInfo, bool) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetObjectInfo", arg0, arg1, arg2)
+	ret := m.ctrl.Call(m, "GetObjectInfo", ctx, bucketName, objectName)
 	ret0, _ := ret[0].(*types0.ObjectInfo)
 	ret1, _ := ret[1].(bool)
 	return ret0, ret1
 }
 
-// GetObjectInfo indicates an expected call of GetObjectInfo
-func (mr *MockStorageKeeperMockRecorder) GetObjectInfo(arg0, arg1, arg2 interface{}) *gomock.Call {
+// GetObjectInfo indicates an expected call of GetObjectInfo.
+func (mr *MockStorageKeeperMockRecorder) GetObjectInfo(ctx, bucketName, objectName interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetObjectInfo", reflect.TypeOf((*MockStorageKeeper)(nil).GetObjectInfo), arg0, arg1, arg2)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetObjectInfo", reflect.TypeOf((*MockStorageKeeper)(nil).GetObjectInfo), ctx, bucketName, objectName)
 }
 
-// GetObjectInfoById mocks base method
-func (m *MockStorageKeeper) GetObjectInfoById(arg0 types1.Context, arg1 math.Uint) (*types0.ObjectInfo, bool) {
+// GetObjectInfoById mocks base method.
+func (m *MockStorageKeeper) GetObjectInfoById(ctx types2.Context, objectId math.Uint) (*types0.ObjectInfo, bool) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetObjectInfoById", arg0, arg1)
+	ret := m.ctrl.Call(m, "GetObjectInfoById", ctx, objectId)
 	ret0, _ := ret[0].(*types0.ObjectInfo)
 	ret1, _ := ret[1].(bool)
 	return ret0, ret1
 }
 
-// GetObjectInfoById indicates an expected call of GetObjectInfoById
-func (mr *MockStorageKeeperMockRecorder) GetObjectInfoById(arg0, arg1 interface{}) *gomock.Call {
+// GetObjectInfoById indicates an expected call of GetObjectInfoById.
+func (mr *MockStorageKeeperMockRecorder) GetObjectInfoById(ctx, objectId interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetObjectInfoById", reflect.TypeOf((*MockStorageKeeper)(nil).GetObjectInfoById), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetObjectInfoById", reflect.TypeOf((*MockStorageKeeper)(nil).GetObjectInfoById), ctx, objectId)
 }
 
-// GetObjectInfoCount mocks base method
-func (m *MockStorageKeeper) GetObjectInfoCount(arg0 types1.Context) math.Uint {
+// GetObjectInfoCount mocks base method.
+func (m *MockStorageKeeper) GetObjectInfoCount(ctx types2.Context) math.Uint {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetObjectInfoCount", arg0)
+	ret := m.ctrl.Call(m, "GetObjectInfoCount", ctx)
 	ret0, _ := ret[0].(math.Uint)
 	return ret0
 }
 
-// GetObjectInfoCount indicates an expected call of GetObjectInfoCount
-func (mr *MockStorageKeeperMockRecorder) GetObjectInfoCount(arg0 interface{}) *gomock.Call {
+// GetObjectInfoCount indicates an expected call of GetObjectInfoCount.
+func (mr *MockStorageKeeperMockRecorder) GetObjectInfoCount(ctx interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetObjectInfoCount", reflect.TypeOf((*MockStorageKeeper)(nil).GetObjectInfoCount), arg0)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetObjectInfoCount", reflect.TypeOf((*MockStorageKeeper)(nil).GetObjectInfoCount), ctx)
 }
 
-// MaxSegmentSize mocks base method
-func (m *MockStorageKeeper) MaxSegmentSize(arg0 types1.Context) uint64 {
+// MaxSegmentSize mocks base method.
+func (m *MockStorageKeeper) MaxSegmentSize(ctx types2.Context) uint64 {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "MaxSegmentSize", arg0)
+	ret := m.ctrl.Call(m, "MaxSegmentSize", ctx)
 	ret0, _ := ret[0].(uint64)
 	return ret0
 }
 
-// MaxSegmentSize indicates an expected call of MaxSegmentSize
-func (mr *MockStorageKeeperMockRecorder) MaxSegmentSize(arg0 interface{}) *gomock.Call {
+// MaxSegmentSize indicates an expected call of MaxSegmentSize.
+func (mr *MockStorageKeeperMockRecorder) MaxSegmentSize(ctx interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MaxSegmentSize", reflect.TypeOf((*MockStorageKeeper)(nil).MaxSegmentSize), ctx)
+}
+
+// MockPaymentKeeper is a mock of PaymentKeeper interface.
+type MockPaymentKeeper struct {
+	ctrl     *gomock.Controller
+	recorder *MockPaymentKeeperMockRecorder
+}
+
+// MockPaymentKeeperMockRecorder is the mock recorder for MockPaymentKeeper.
+type MockPaymentKeeperMockRecorder struct {
+	mock *MockPaymentKeeper
+}
+
+// NewMockPaymentKeeper creates a new mock instance.
+func NewMockPaymentKeeper(ctrl *gomock.Controller) *MockPaymentKeeper {
+	mock := &MockPaymentKeeper{ctrl: ctrl}
+	mock.recorder = &MockPaymentKeeperMockRecorder{mock}
+	return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockPaymentKeeper) EXPECT() *MockPaymentKeeperMockRecorder {
+	return m.recorder
+}
+
+// QueryDynamicBalance mocks base method.
+func (m *MockPaymentKeeper) QueryDynamicBalance(ctx types2.Context, addr types2.AccAddress) (math.Int, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "QueryDynamicBalance", ctx, addr)
+	ret0, _ := ret[0].(math.Int)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// QueryDynamicBalance indicates an expected call of QueryDynamicBalance.
+func (mr *MockPaymentKeeperMockRecorder) QueryDynamicBalance(ctx, addr interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryDynamicBalance", reflect.TypeOf((*MockPaymentKeeper)(nil).QueryDynamicBalance), ctx, addr)
+}
+
+// Withdraw mocks base method.
+func (m *MockPaymentKeeper) Withdraw(ctx types2.Context, fromAddr, toAddr types2.AccAddress, amount math.Int) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Withdraw", ctx, fromAddr, toAddr, amount)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// Withdraw indicates an expected call of Withdraw.
+func (mr *MockPaymentKeeperMockRecorder) Withdraw(ctx, fromAddr, toAddr, amount interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Withdraw", reflect.TypeOf((*MockPaymentKeeper)(nil).Withdraw), ctx, fromAddr, toAddr, amount)
+}
+
+// MockAccountKeeper is a mock of AccountKeeper interface.
+type MockAccountKeeper struct {
+	ctrl     *gomock.Controller
+	recorder *MockAccountKeeperMockRecorder
+}
+
+// MockAccountKeeperMockRecorder is the mock recorder for MockAccountKeeper.
+type MockAccountKeeperMockRecorder struct {
+	mock *MockAccountKeeper
+}
+
+// NewMockAccountKeeper creates a new mock instance.
+func NewMockAccountKeeper(ctrl *gomock.Controller) *MockAccountKeeper {
+	mock := &MockAccountKeeper{ctrl: ctrl}
+	mock.recorder = &MockAccountKeeperMockRecorder{mock}
+	return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockAccountKeeper) EXPECT() *MockAccountKeeperMockRecorder {
+	return m.recorder
+}
+
+// GetAccount mocks base method.
+func (m *MockAccountKeeper) GetAccount(ctx types2.Context, addr types2.AccAddress) types3.AccountI {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetAccount", ctx, addr)
+	ret0, _ := ret[0].(types3.AccountI)
+	return ret0
+}
+
+// GetAccount indicates an expected call of GetAccount.
+func (mr *MockAccountKeeperMockRecorder) GetAccount(ctx, addr interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetAccount), ctx, addr)
+}
+
+// MockBankKeeper is a mock of BankKeeper interface.
+type MockBankKeeper struct {
+	ctrl     *gomock.Controller
+	recorder *MockBankKeeperMockRecorder
+}
+
+// MockBankKeeperMockRecorder is the mock recorder for MockBankKeeper.
+type MockBankKeeperMockRecorder struct {
+	mock *MockBankKeeper
+}
+
+// NewMockBankKeeper creates a new mock instance.
+func NewMockBankKeeper(ctrl *gomock.Controller) *MockBankKeeper {
+	mock := &MockBankKeeper{ctrl: ctrl}
+	mock.recorder = &MockBankKeeperMockRecorder{mock}
+	return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockBankKeeper) EXPECT() *MockBankKeeperMockRecorder {
+	return m.recorder
+}
+
+// SpendableCoins mocks base method.
+func (m *MockBankKeeper) SpendableCoins(ctx types2.Context, addr types2.AccAddress) types2.Coins {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "SpendableCoins", ctx, addr)
+	ret0, _ := ret[0].(types2.Coins)
+	return ret0
+}
+
+// SpendableCoins indicates an expected call of SpendableCoins.
+func (mr *MockBankKeeperMockRecorder) SpendableCoins(ctx, addr interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MaxSegmentSize", reflect.TypeOf((*MockStorageKeeper)(nil).MaxSegmentSize), arg0)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpendableCoins", reflect.TypeOf((*MockBankKeeper)(nil).SpendableCoins), ctx, addr)
 }
diff --git a/x/challenge/types/message_attest.go b/x/challenge/types/message_attest.go
index 99b416377..45e3afb58 100644
--- a/x/challenge/types/message_attest.go
+++ b/x/challenge/types/message_attest.go
@@ -80,7 +80,7 @@ func (msg *MsgAttest) ValidateBasic() error {
 	return nil
 }
 
-func (msg *MsgAttest) GetBlsSignBytes() [32]byte {
+func (msg *MsgAttest) GetBlsSignBytes(chainId string) [32]byte {
 	challengeIdBz := make([]byte, 8)
 	binary.BigEndian.PutUint64(challengeIdBz, msg.ChallengeId)
 	objectIdBz := msg.ObjectId.Bytes()
@@ -94,6 +94,7 @@ func (msg *MsgAttest) GetBlsSignBytes() [32]byte {
 	}
 
 	bs := make([]byte, 0)
+	bs = append(bs, []byte(chainId)...)
 	bs = append(bs, challengeIdBz...)
 	bs = append(bs, objectIdBz...)
 	bs = append(bs, resultBz...)
diff --git a/x/challenge/types/types.pb.go b/x/challenge/types/types.pb.go
index a4633fb3c..5026cb985 100644
--- a/x/challenge/types/types.pb.go
+++ b/x/challenge/types/types.pb.go
@@ -55,7 +55,7 @@ func (VoteResult) EnumDescriptor() ([]byte, []int) {
 // Slash records the storage provider slashes, which will be pruned periodically.
 type Slash struct {
 	// The slashed storage provider.
-	SpOperatorAddress []byte `protobuf:"bytes,1,opt,name=sp_operator_address,json=spOperatorAddress,proto3" json:"sp_operator_address,omitempty"`
+	SpId uint32 `protobuf:"varint,1,opt,name=sp_id,json=spId,proto3" json:"sp_id,omitempty"`
 	// The slashed object info.
 	ObjectId Uint `protobuf:"bytes,2,opt,name=object_id,json=objectId,proto3,customtype=Uint" json:"object_id"`
 	// The height when the slash happened, which is used for prune purpose.
@@ -95,11 +95,11 @@ func (m *Slash) XXX_DiscardUnknown() {
 
 var xxx_messageInfo_Slash proto.InternalMessageInfo
 
-func (m *Slash) GetSpOperatorAddress() []byte {
+func (m *Slash) GetSpId() uint32 {
 	if m != nil {
-		return m.SpOperatorAddress
+		return m.SpId
 	}
-	return nil
+	return 0
 }
 
 func (m *Slash) GetHeight() uint64 {
@@ -295,36 +295,35 @@ func init() {
 func init() { proto.RegisterFile("greenfield/challenge/types.proto", fileDescriptor_9c297f0764c47d40) }
 
 var fileDescriptor_9c297f0764c47d40 = []byte{
-	// 453 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x52, 0xd1, 0x8a, 0xd3, 0x40,
-	0x14, 0xcd, 0xa4, 0xb1, 0xd8, 0xbb, 0x5a, 0xda, 0xb1, 0x4a, 0xad, 0x90, 0x0d, 0x05, 0xb1, 0x08,
-	0x9b, 0xe0, 0xfa, 0xb2, 0x6f, 0xd2, 0x66, 0xe3, 0x6e, 0xb1, 0x28, 0x64, 0x59, 0x1f, 0x04, 0x09,
-	0x49, 0x66, 0x4c, 0x46, 0xb2, 0x99, 0x30, 0x33, 0x85, 0xd5, 0x2f, 0x50, 0x7c, 0xf1, 0x1f, 0xfc,
-	0x05, 0x3f, 0x62, 0x1f, 0x17, 0x9f, 0xc4, 0x87, 0x45, 0xda, 0x1f, 0x91, 0x26, 0x71, 0xbb, 0xb8,
-	0xf5, 0x6d, 0xce, 0xb9, 0xe7, 0x9e, 0x7b, 0xe7, 0x72, 0xc0, 0x4a, 0x04, 0xa5, 0xf9, 0x3b, 0x46,
-	0x33, 0xe2, 0xc4, 0x69, 0x98, 0x65, 0x34, 0x4f, 0xa8, 0xa3, 0x3e, 0x14, 0x54, 0xda, 0x85, 0xe0,
-	0x8a, 0xe3, 0xde, 0x5a, 0x61, 0x5f, 0x2a, 0x06, 0xf7, 0x63, 0x2e, 0x4f, 0xb8, 0x0c, 0x4a, 0x8d,
-	0x53, 0x81, 0xaa, 0x61, 0xd0, 0x4b, 0x78, 0xc2, 0x2b, 0x7e, 0xf5, 0xaa, 0xd8, 0xe1, 0x67, 0x04,
-	0x37, 0x8e, 0xb2, 0x50, 0xa6, 0xd8, 0x86, 0x3b, 0xb2, 0x08, 0x78, 0x41, 0x45, 0xa8, 0xb8, 0x08,
-	0x42, 0x42, 0x04, 0x95, 0xb2, 0x8f, 0x2c, 0x34, 0xba, 0xe5, 0x77, 0x65, 0xf1, 0xaa, 0xae, 0x8c,
-	0xab, 0x02, 0xde, 0x83, 0x16, 0x8f, 0xde, 0xd3, 0x58, 0x05, 0x8c, 0xf4, 0x75, 0x0b, 0x8d, 0x5a,
-	0x93, 0x07, 0x67, 0x17, 0xdb, 0xda, 0xaf, 0x8b, 0x6d, 0xe3, 0x98, 0xe5, 0xea, 0xc7, 0xf7, 0x9d,
-	0xad, 0x7a, 0x81, 0x15, 0xf4, 0x6f, 0x56, 0xea, 0x29, 0xc1, 0xf7, 0xa0, 0x99, 0x52, 0x96, 0xa4,
-	0xaa, 0xdf, 0xb0, 0xd0, 0xc8, 0xf0, 0x6b, 0x34, 0x9c, 0x40, 0xcb, 0xfd, 0xfb, 0x13, 0xdc, 0x06,
-	0x9d, 0x91, 0x72, 0xba, 0xe1, 0xeb, 0x8c, 0xe0, 0x87, 0xd0, 0xa6, 0xa7, 0x05, 0x13, 0x94, 0x04,
-	0x75, 0xb3, 0x5e, 0xd6, 0x6e, 0xd7, 0xec, 0x61, 0xe5, 0xf1, 0x16, 0xba, 0x63, 0xa5, 0xa8, 0x54,
-	0x94, 0xfc, 0xdf, 0x6b, 0x0f, 0x9a, 0x82, 0xca, 0x79, 0x56, 0x79, 0xb4, 0x77, 0x2d, 0x7b, 0xd3,
-	0x31, 0xed, 0xd7, 0x5c, 0x51, 0xbf, 0xd4, 0xf9, 0xb5, 0x7e, 0xf8, 0x05, 0x41, 0xef, 0x9a, 0xff,
-	0x94, 0x48, 0x8c, 0xc1, 0x90, 0xec, 0x23, 0xad, 0x87, 0x94, 0x6f, 0x7c, 0x00, 0x70, 0x69, 0x26,
-	0xfb, 0xba, 0xd5, 0x18, 0x6d, 0xed, 0x3e, 0xda, 0x3c, 0xea, 0x9a, 0xa7, 0x7f, 0xa5, 0x75, 0x75,
-	0xb0, 0x78, 0x2e, 0x24, 0x17, 0xe5, 0xc1, 0x1a, 0x7e, 0x8d, 0x1e, 0x3f, 0x03, 0x58, 0xef, 0x88,
-	0x7b, 0xd0, 0x71, 0x0f, 0xc7, 0xb3, 0x99, 0xf7, 0xf2, 0xc0, 0x0b, 0x9e, 0x8f, 0xa7, 0x33, 0x6f,
-	0xbf, 0xa3, 0xe1, 0xbb, 0xd0, 0x5d, 0xb3, 0x47, 0xc7, 0xae, 0xeb, 0x79, 0xfb, 0x1d, 0x34, 0x30,
-	0x3e, 0x7d, 0x33, 0xb5, 0xc9, 0x8b, 0xb3, 0x85, 0x89, 0xce, 0x17, 0x26, 0xfa, 0xbd, 0x30, 0xd1,
-	0xd7, 0xa5, 0xa9, 0x9d, 0x2f, 0x4d, 0xed, 0xe7, 0xd2, 0xd4, 0xde, 0x3c, 0x49, 0x98, 0x4a, 0xe7,
-	0x91, 0x1d, 0xf3, 0x13, 0x27, 0xca, 0xa3, 0x9d, 0x38, 0x0d, 0x59, 0xee, 0x5c, 0x49, 0xe5, 0xe9,
-	0xbf, 0xb9, 0x8c, 0x9a, 0x65, 0xa2, 0x9e, 0xfe, 0x09, 0x00, 0x00, 0xff, 0xff, 0x11, 0x69, 0x04,
-	0x27, 0xbc, 0x02, 0x00, 0x00,
+	// 436 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x52, 0xc1, 0x6a, 0xdb, 0x40,
+	0x10, 0xd5, 0xca, 0x8a, 0xa9, 0x27, 0xc4, 0x38, 0x5b, 0xb7, 0xb8, 0x2e, 0x28, 0xc2, 0x50, 0x6a,
+	0x0a, 0x91, 0x68, 0x7a, 0xc9, 0xad, 0xd8, 0x8e, 0x9a, 0x98, 0x9a, 0x1e, 0x36, 0xa4, 0x87, 0x42,
+	0x11, 0x96, 0x76, 0x2b, 0x6d, 0x51, 0xb4, 0x42, 0xbb, 0x86, 0xb4, 0x5f, 0x50, 0xe8, 0xa5, 0xff,
+	0xd0, 0x5f, 0xe8, 0x47, 0xe4, 0x18, 0x7a, 0x2a, 0x3d, 0x84, 0x62, 0xff, 0x48, 0x91, 0xb4, 0x8d,
+	0x43, 0x93, 0xdc, 0xe6, 0xbd, 0x79, 0xf3, 0xde, 0x30, 0x0c, 0x38, 0x71, 0xc1, 0x58, 0xf6, 0x81,
+	0xb3, 0x94, 0x7a, 0x51, 0x32, 0x4f, 0x53, 0x96, 0xc5, 0xcc, 0x53, 0x9f, 0x72, 0x26, 0xdd, 0xbc,
+	0x10, 0x4a, 0xe0, 0xee, 0x5a, 0xe1, 0x5e, 0x29, 0xfa, 0x8f, 0x22, 0x21, 0x4f, 0x85, 0x0c, 0x2a,
+	0x8d, 0x57, 0x83, 0x7a, 0xa0, 0xdf, 0x8d, 0x45, 0x2c, 0x6a, 0xbe, 0xac, 0x6a, 0x76, 0x90, 0xc1,
+	0xc6, 0x71, 0x3a, 0x97, 0x09, 0xbe, 0x0f, 0x1b, 0x32, 0x0f, 0x38, 0xed, 0x21, 0x07, 0x0d, 0xb7,
+	0x88, 0x25, 0xf3, 0x29, 0xc5, 0xfb, 0xd0, 0x12, 0xe1, 0x47, 0x16, 0xa9, 0xb2, 0x61, 0x3a, 0x68,
+	0xd8, 0x1a, 0x3f, 0x3e, 0xbf, 0xdc, 0x31, 0x7e, 0x5f, 0xee, 0x58, 0x27, 0x3c, 0x53, 0x3f, 0x7f,
+	0xec, 0x6e, 0xea, 0x90, 0x12, 0x92, 0x7b, 0xb5, 0x7a, 0x4a, 0xf1, 0x43, 0x68, 0x26, 0x8c, 0xc7,
+	0x89, 0xea, 0x35, 0x1c, 0x34, 0xb4, 0x88, 0x46, 0x83, 0x31, 0xb4, 0x26, 0xff, 0xb6, 0xc5, 0x6d,
+	0x30, 0x75, 0xa0, 0x45, 0x4c, 0x4e, 0xf1, 0x13, 0x68, 0xb3, 0xb3, 0x9c, 0x17, 0x8c, 0x06, 0x7a,
+	0xd8, 0xac, 0x7a, 0x5b, 0x9a, 0x3d, 0xaa, 0x3d, 0xde, 0xc3, 0xf6, 0x48, 0x29, 0x26, 0x15, 0xa3,
+	0x77, 0x7b, 0xed, 0x43, 0xb3, 0x60, 0x72, 0x91, 0xd6, 0x1e, 0xed, 0x3d, 0xc7, 0xbd, 0xed, 0x60,
+	0xee, 0x5b, 0xa1, 0x18, 0xa9, 0x74, 0x44, 0xeb, 0x07, 0x5f, 0x11, 0x74, 0x6f, 0xf8, 0x4f, 0xa9,
+	0xc4, 0x18, 0x2c, 0xc9, 0x3f, 0x33, 0x1d, 0x52, 0xd5, 0xf8, 0x10, 0xe0, 0xca, 0x4c, 0xf6, 0x4c,
+	0xa7, 0x31, 0xdc, 0xdc, 0x7b, 0x7a, 0x7b, 0xd4, 0x0d, 0x4f, 0x72, 0x6d, 0xb4, 0x3c, 0x58, 0xb4,
+	0x28, 0xa4, 0x28, 0xaa, 0x83, 0x35, 0x88, 0x46, 0xcf, 0x5e, 0x02, 0xac, 0x77, 0xc4, 0x5d, 0xe8,
+	0x4c, 0x8e, 0x46, 0xb3, 0x99, 0xff, 0xe6, 0xd0, 0x0f, 0x5e, 0x8d, 0xa6, 0x33, 0xff, 0xa0, 0x63,
+	0xe0, 0x07, 0xb0, 0xbd, 0x66, 0x8f, 0x4f, 0x26, 0x13, 0xdf, 0x3f, 0xe8, 0xa0, 0xbe, 0xf5, 0xe5,
+	0xbb, 0x6d, 0x8c, 0x5f, 0x9f, 0x2f, 0x6d, 0x74, 0xb1, 0xb4, 0xd1, 0x9f, 0xa5, 0x8d, 0xbe, 0xad,
+	0x6c, 0xe3, 0x62, 0x65, 0x1b, 0xbf, 0x56, 0xb6, 0xf1, 0xee, 0x79, 0xcc, 0x55, 0xb2, 0x08, 0xdd,
+	0x48, 0x9c, 0x7a, 0x61, 0x16, 0xee, 0x46, 0xc9, 0x9c, 0x67, 0xde, 0xb5, 0xcf, 0x3b, 0xfb, 0xff,
+	0xf7, 0xc2, 0x66, 0xf5, 0x35, 0x2f, 0xfe, 0x06, 0x00, 0x00, 0xff, 0xff, 0x68, 0xaf, 0x39, 0x21,
+	0xa0, 0x02, 0x00, 0x00,
 }
 
 func (m *Slash) Marshal() (dAtA []byte, err error) {
@@ -362,12 +361,10 @@ func (m *Slash) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	}
 	i--
 	dAtA[i] = 0x12
-	if len(m.SpOperatorAddress) > 0 {
-		i -= len(m.SpOperatorAddress)
-		copy(dAtA[i:], m.SpOperatorAddress)
-		i = encodeVarintTypes(dAtA, i, uint64(len(m.SpOperatorAddress)))
+	if m.SpId != 0 {
+		i = encodeVarintTypes(dAtA, i, uint64(m.SpId))
 		i--
-		dAtA[i] = 0xa
+		dAtA[i] = 0x8
 	}
 	return len(dAtA) - i, nil
 }
@@ -502,9 +499,8 @@ func (m *Slash) Size() (n int) {
 	}
 	var l int
 	_ = l
-	l = len(m.SpOperatorAddress)
-	if l > 0 {
-		n += 1 + l + sovTypes(uint64(l))
+	if m.SpId != 0 {
+		n += 1 + sovTypes(uint64(m.SpId))
 	}
 	l = m.ObjectId.Size()
 	n += 1 + l + sovTypes(uint64(l))
@@ -601,10 +597,10 @@ func (m *Slash) Unmarshal(dAtA []byte) error {
 		}
 		switch fieldNum {
 		case 1:
-			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field SpOperatorAddress", wireType)
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field SpId", wireType)
 			}
-			var byteLen int
+			m.SpId = 0
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowTypes
@@ -614,26 +610,11 @@ func (m *Slash) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				byteLen |= int(b&0x7F) << shift
+				m.SpId |= uint32(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
-			if byteLen < 0 {
-				return ErrInvalidLengthTypes
-			}
-			postIndex := iNdEx + byteLen
-			if postIndex < 0 {
-				return ErrInvalidLengthTypes
-			}
-			if postIndex > l {
-				return io.ErrUnexpectedEOF
-			}
-			m.SpOperatorAddress = append(m.SpOperatorAddress[:0], dAtA[iNdEx:postIndex]...)
-			if m.SpOperatorAddress == nil {
-				m.SpOperatorAddress = []byte{}
-			}
-			iNdEx = postIndex
 		case 2:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field ObjectId", wireType)
diff --git a/x/gensp/client/cli/gentx.go b/x/gensp/client/cli/gentx.go
index 5ad72a189..81733e07b 100644
--- a/x/gensp/client/cli/gentx.go
+++ b/x/gensp/client/cli/gentx.go
@@ -49,6 +49,8 @@ $ %s gentx sp0 10000000000000000000000000BNB --home ./deployment/localup/.local/
 	--operator-address=0x76330E9C31D8B91a8247a9bbA2959815D3008417 \
 	--funding-address=0x52C30AA52788ec9C8F36C3774C1F50702BCa59b9 \
 	--seal-address=0x419D46b3aA67Dc9075c4FEC4c456fd29697CB897 \
+    --bls-pub-key=84a22236c7859ba4e52f43412801b30d3ad1d2f23324a26b001646f30c299357cacb6ccfc017854d9830db8e63639ce6 \
+    --bls-proof=987406f0dd2e5f5f3e9e3e447f832dbf73809479ea552fe9b23e06e65651c01a5893e92a797d12078ef9b0c9eb72b8d90faaee65f738e222793d94b80a58cd0f329375ee04e8460f12f4772cf42859d30dd6444ed3350c3335aedf1dbde3bb68 \
 	--approval-address=0x68a60866C1e98e277a7389c9Ad90c10cb56debc9 \
 	--keyring-backend=test --chain-id=greenfield_9000-121 \
 	--moniker=sp0 --details=sp0 --website=http://website --endpoint="http://127.0.0.1:9033" \
diff --git a/x/gensp/gentx_test.go b/x/gensp/gentx_test.go
index 87a4a1f07..08d876fcb 100644
--- a/x/gensp/gentx_test.go
+++ b/x/gensp/gentx_test.go
@@ -1,7 +1,6 @@
 package gensp_test
 
 import (
-	"encoding/hex"
 	"encoding/json"
 	"fmt"
 	"math/rand"
@@ -22,9 +21,9 @@ import (
 	genutiltestutil "github.com/cosmos/cosmos-sdk/x/genutil/testutil"
 	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
 	"github.com/golang/mock/gomock"
-	"github.com/prysmaticlabs/prysm/crypto/bls"
 	"github.com/stretchr/testify/suite"
 
+	"github.com/bnb-chain/greenfield/testutil/sample"
 	"github.com/bnb-chain/greenfield/x/gensp"
 	gensptypes "github.com/bnb-chain/greenfield/x/gensp/types"
 )
@@ -66,19 +65,20 @@ func (suite *GenTxTestSuite) SetupTest() {
 	var err error
 	amount := sdk.NewInt64Coin(sdk.DefaultBondDenom, 50)
 	one := math.OneInt()
-	blsSecretKey, _ := bls.RandKey()
-	blsPk := hex.EncodeToString(blsSecretKey.PublicKey().Marshal())
+	blsPubKey, blsProof := sample.RandBlsPubKeyAndBlsProof()
 	suite.msg1, err = stakingtypes.NewMsgCreateValidator(
 		sdk.AccAddress(pk1.Address()), pk1,
 		amount, desc, comm, one,
 		sdk.AccAddress(pk1.Address()), sdk.AccAddress(pk1.Address()),
-		sdk.AccAddress(pk1.Address()), sdk.AccAddress(pk1.Address()), blsPk)
+		sdk.AccAddress(pk1.Address()), sdk.AccAddress(pk1.Address()),
+		blsPubKey, blsProof)
 	suite.NoError(err)
 	suite.msg2, err = stakingtypes.NewMsgCreateValidator(
 		sdk.AccAddress(pk2.Address()), pk1,
 		amount, desc, comm, one,
 		sdk.AccAddress(pk2.Address()), sdk.AccAddress(pk2.Address()),
-		sdk.AccAddress(pk2.Address()), sdk.AccAddress(pk1.Address()), blsPk)
+		sdk.AccAddress(pk2.Address()), sdk.AccAddress(pk1.Address()),
+		blsPubKey, blsProof)
 	suite.NoError(err)
 }
 
diff --git a/x/payment/keeper/auto_resume_record.go b/x/payment/keeper/auto_resume_record.go
new file mode 100644
index 000000000..2654e529e
--- /dev/null
+++ b/x/payment/keeper/auto_resume_record.go
@@ -0,0 +1,79 @@
+package keeper
+
+import (
+	"github.com/cosmos/cosmos-sdk/store/prefix"
+	storetypes "github.com/cosmos/cosmos-sdk/store/types"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+
+	"github.com/bnb-chain/greenfield/x/payment/types"
+)
+
+// SetAutoResumeRecord set a specific autoResumeRecord in the store from its index
+func (k Keeper) SetAutoResumeRecord(ctx sdk.Context, autoResumeRecord *types.AutoResumeRecord) {
+	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.AutoResumeRecordKeyPrefix)
+	b := []byte{0x00}
+	store.Set(types.AutoResumeRecordKey(
+		autoResumeRecord.Timestamp,
+		sdk.MustAccAddressFromHex(autoResumeRecord.Addr),
+	), b)
+}
+
+// GetAutoResumeRecord returns a autoResumeRecord from its index
+func (k Keeper) GetAutoResumeRecord(
+	ctx sdk.Context,
+	timestamp int64,
+	addr sdk.AccAddress,
+) (*types.AutoResumeRecord, bool) {
+	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.AutoResumeRecordKeyPrefix)
+
+	b := store.Get(types.AutoResumeRecordKey(
+		timestamp,
+		addr,
+	))
+	if b == nil {
+		return nil, false
+	}
+
+	return &types.AutoResumeRecord{
+		Timestamp: timestamp,
+		Addr:      addr.String(),
+	}, true
+}
+
+// RemoveAutoResumeRecord removes a autoResumeRecord from the store
+func (k Keeper) RemoveAutoResumeRecord(
+	ctx sdk.Context,
+	timestamp int64,
+	addr sdk.AccAddress,
+) {
+	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.AutoResumeRecordKeyPrefix)
+	store.Delete(types.AutoResumeRecordKey(
+		timestamp,
+		addr,
+	))
+}
+
+// ExistsAutoResumeRecord checks whether there exists a autoResumeRecord
+func (k Keeper) ExistsAutoResumeRecord(
+	ctx sdk.Context,
+	timestamp int64,
+	addr sdk.AccAddress,
+) bool {
+	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.AutoResumeRecordKeyPrefix)
+	iterator := storetypes.KVStorePrefixIterator(store, []byte{})
+	defer iterator.Close()
+
+	exists := false
+	for ; iterator.Valid(); iterator.Next() {
+		record := types.ParseAutoResumeRecordKey(iterator.Key())
+		if record.Timestamp > timestamp {
+			break
+		}
+		if sdk.MustAccAddressFromHex(record.Addr).Equals(addr) {
+			exists = true
+			break
+		}
+	}
+
+	return exists
+}
diff --git a/x/payment/keeper/auto_settle_record.go b/x/payment/keeper/auto_settle_record.go
index a9238c557..7c5c5f7c9 100644
--- a/x/payment/keeper/auto_settle_record.go
+++ b/x/payment/keeper/auto_settle_record.go
@@ -23,7 +23,7 @@ func (k Keeper) GetAutoSettleRecord(
 	ctx sdk.Context,
 	timestamp int64,
 	addr sdk.AccAddress,
-) (val *types.AutoSettleRecord, found bool) {
+) (*types.AutoSettleRecord, bool) {
 	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.AutoSettleRecordKeyPrefix)
 
 	b := store.Get(types.AutoSettleRecordKey(
@@ -31,14 +31,13 @@ func (k Keeper) GetAutoSettleRecord(
 		addr,
 	))
 	if b == nil {
-		return val, false
+		return nil, false
 	}
 
-	val = &types.AutoSettleRecord{
+	return &types.AutoSettleRecord{
 		Timestamp: timestamp,
 		Addr:      addr.String(),
-	}
-	return val, true
+	}, true
 }
 
 // RemoveAutoSettleRecord removes a autoSettleRecord from the store
diff --git a/x/payment/keeper/grpc_query_out_flow.go b/x/payment/keeper/grpc_query_out_flow.go
new file mode 100644
index 000000000..023a1b950
--- /dev/null
+++ b/x/payment/keeper/grpc_query_out_flow.go
@@ -0,0 +1,25 @@
+package keeper
+
+import (
+	"context"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
+
+	"github.com/bnb-chain/greenfield/x/payment/types"
+)
+
+func (k Keeper) OutFlows(c context.Context, req *types.QueryOutFlowsRequest) (*types.QueryOutFlowsResponse, error) {
+	if req == nil {
+		return nil, status.Error(codes.InvalidArgument, "invalid request")
+	}
+	ctx := sdk.UnwrapSDKContext(c)
+
+	account, err := sdk.AccAddressFromHexUnsafe(req.Account)
+	if err != nil {
+		return nil, status.Error(codes.InvalidArgument, "invalid account")
+	}
+
+	return &types.QueryOutFlowsResponse{OutFlows: k.GetOutFlows(ctx, account)}, nil
+}
diff --git a/x/payment/keeper/keeper.go b/x/payment/keeper/keeper.go
index 81840ce09..78f12acbd 100644
--- a/x/payment/keeper/keeper.go
+++ b/x/payment/keeper/keeper.go
@@ -53,7 +53,7 @@ func (k Keeper) QueryDynamicBalance(ctx sdk.Context, addr sdk.AccAddress) (amoun
 		return sdkmath.ZeroInt(), nil
 	}
 	change := types.NewDefaultStreamRecordChangeWithAddr(addr)
-	err = k.UpdateStreamRecord(ctx, streamRecord, change, false)
+	err = k.UpdateStreamRecord(ctx, streamRecord, change)
 	if err != nil {
 		return sdkmath.ZeroInt(), errors.Wrapf(err, "update stream record failed")
 	}
@@ -63,17 +63,17 @@ func (k Keeper) QueryDynamicBalance(ctx sdk.Context, addr sdk.AccAddress) (amoun
 func (k Keeper) Withdraw(ctx sdk.Context, fromAddr, toAddr sdk.AccAddress, amount sdkmath.Int) error {
 	streamRecord, found := k.GetStreamRecord(ctx, fromAddr)
 	if !found {
-		return errors.Wrapf(types.ErrStreamRecordNotFound, "validator tax pool stream record not found")
+		return errors.Wrapf(types.ErrStreamRecordNotFound, "stream record not found %s", fromAddr.String())
 	}
 	change := types.NewDefaultStreamRecordChangeWithAddr(fromAddr).WithStaticBalanceChange(amount.Neg())
-	err := k.UpdateStreamRecord(ctx, streamRecord, change, false)
+	err := k.UpdateStreamRecord(ctx, streamRecord, change)
 	if err != nil {
-		return errors.Wrapf(err, "update stream record failed")
+		return errors.Wrapf(err, "update stream record failed %s", fromAddr.String())
 	}
 	k.SetStreamRecord(ctx, streamRecord)
 	err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, toAddr, sdk.NewCoins(sdk.NewCoin(k.GetParams(ctx).FeeDenom, amount)))
 	if err != nil {
-		return errors.Wrapf(err, "send coins from module to account failed")
+		return errors.Wrapf(err, "send coins from module to account failed %s", toAddr.String())
 	}
 	return nil
 }
diff --git a/x/payment/keeper/msg_server_deposit.go b/x/payment/keeper/msg_server_deposit.go
index f7277502a..3ee98d4b2 100644
--- a/x/payment/keeper/msg_server_deposit.go
+++ b/x/payment/keeper/msg_server_deposit.go
@@ -36,7 +36,7 @@ func (k msgServer) Deposit(goCtx context.Context, msg *types.MsgDeposit) (*types
 		if streamRecord.Status == types.STREAM_ACCOUNT_STATUS_ACTIVE {
 			// add static balance
 			change := types.NewDefaultStreamRecordChangeWithAddr(to).WithStaticBalanceChange(msg.Amount)
-			err = k.UpdateStreamRecord(ctx, streamRecord, change, false)
+			err = k.UpdateStreamRecord(ctx, streamRecord, change)
 			if err != nil {
 				return nil, err
 			}
diff --git a/x/payment/keeper/msg_server_withdraw.go b/x/payment/keeper/msg_server_withdraw.go
index d17b40b35..8d8cec396 100644
--- a/x/payment/keeper/msg_server_withdraw.go
+++ b/x/payment/keeper/msg_server_withdraw.go
@@ -31,11 +31,11 @@ func (k msgServer) Withdraw(goCtx context.Context, msg *types.MsgWithdraw) (*typ
 		}
 	}
 	change := types.NewDefaultStreamRecordChangeWithAddr(from).WithStaticBalanceChange(msg.Amount.Neg())
-	err := k.UpdateStreamRecord(ctx, streamRecord, change, false)
-	k.SetStreamRecord(ctx, streamRecord)
+	err := k.UpdateStreamRecord(ctx, streamRecord, change)
 	if err != nil {
 		return nil, err
 	}
+	k.SetStreamRecord(ctx, streamRecord)
 	if streamRecord.StaticBalance.IsNegative() {
 		return nil, errors.Wrapf(types.ErrInsufficientBalance, "static balance: %s after withdraw", streamRecord.StaticBalance)
 	}
diff --git a/x/payment/keeper/out_flow.go b/x/payment/keeper/out_flow.go
new file mode 100644
index 000000000..ab76aec05
--- /dev/null
+++ b/x/payment/keeper/out_flow.go
@@ -0,0 +1,88 @@
+package keeper
+
+import (
+	"github.com/cosmos/cosmos-sdk/store/prefix"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+
+	"github.com/bnb-chain/greenfield/x/payment/types"
+)
+
+// SetOutFlow set a specific OutFlow in the store from its index
+func (k Keeper) SetOutFlow(ctx sdk.Context, addr sdk.AccAddress, outFlow *types.OutFlow) {
+	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.OutFlowKeyPrefix)
+	bz, err := outFlow.Rate.Marshal()
+	if err != nil {
+		panic("should not happen")
+	}
+	store.Set(types.OutFlowKey(
+		addr,
+		outFlow.Status,
+		sdk.MustAccAddressFromHex(outFlow.ToAddress),
+	), bz)
+}
+
+// GetOutFlow get a specific OutFlow in the store from its index
+func (k Keeper) GetOutFlow(ctx sdk.Context, addr sdk.AccAddress, status types.OutFlowStatus, toAddr sdk.AccAddress) *types.OutFlow {
+	key := types.OutFlowKey(addr, status, toAddr)
+	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.OutFlowKeyPrefix)
+
+	value := store.Get(key)
+	if value == nil {
+		return nil
+	}
+
+	return &types.OutFlow{
+		ToAddress: toAddr.String(),
+		Rate:      types.ParseOutFlowValue(value),
+		Status:    status,
+	}
+}
+
+// GetOutFlows get OutFlows for a specific from address
+func (k Keeper) GetOutFlows(ctx sdk.Context, addr sdk.AccAddress) []types.OutFlow {
+	key := types.OutFlowKey(addr, types.OUT_FLOW_STATUS_ACTIVE, nil)
+	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.OutFlowKeyPrefix)
+	iterator := store.Iterator(key, nil) //the iterator will also include frozen out flows
+	defer iterator.Close()
+
+	outFlows := make([]types.OutFlow, 0)
+	for ; iterator.Valid(); iterator.Next() {
+		addrInKey, outFlow := types.ParseOutFlowKey(iterator.Key())
+		if !addrInKey.Equals(addr) {
+			break
+		}
+		outFlow.Rate = types.ParseOutFlowValue(iterator.Value())
+		outFlows = append(outFlows, outFlow)
+	}
+	return outFlows
+}
+
+// DeleteOutFlow set a specific OutFlow from the store
+func (k Keeper) DeleteOutFlow(ctx sdk.Context, key []byte) {
+	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.OutFlowKeyPrefix)
+	store.Delete(key)
+}
+
+// MergeActiveOutFlows merge active OutFlows and save them in the store
+func (k Keeper) MergeActiveOutFlows(ctx sdk.Context, addr sdk.AccAddress, outFlows []types.OutFlow) int {
+	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.OutFlowKeyPrefix)
+	deltaCount := 0
+	for _, outFlow := range outFlows {
+		outFlow.Status = types.OUT_FLOW_STATUS_ACTIVE
+		key := types.OutFlowKey(addr, outFlow.Status, sdk.MustAccAddressFromHex(outFlow.ToAddress))
+		value := store.Get(key)
+		if value == nil {
+			k.SetOutFlow(ctx, addr, &outFlow)
+			deltaCount++
+			continue
+		}
+		outFlow.Rate = types.ParseOutFlowValue(value).Add(outFlow.Rate)
+		if outFlow.Rate.IsZero() {
+			k.DeleteOutFlow(ctx, key)
+			deltaCount--
+		} else {
+			k.SetOutFlow(ctx, addr, &outFlow)
+		}
+	}
+	return deltaCount
+}
diff --git a/x/payment/keeper/out_flow_test.go b/x/payment/keeper/out_flow_test.go
new file mode 100644
index 000000000..cfbc10819
--- /dev/null
+++ b/x/payment/keeper/out_flow_test.go
@@ -0,0 +1,99 @@
+package keeper_test
+
+import (
+	"testing"
+
+	"cosmossdk.io/math"
+	"github.com/stretchr/testify/require"
+
+	"github.com/bnb-chain/greenfield/testutil/sample"
+	"github.com/bnb-chain/greenfield/x/payment/types"
+)
+
+func TestMergeActiveOutFlows(t *testing.T) {
+	keeper, ctx, _ := makePaymentKeeper(t)
+
+	addr := sample.RandAccAddress()
+
+	toAddr1 := sample.RandAccAddress()
+	toAddr1Rate := math.NewInt(10)
+	outFlow1 := types.OutFlow{
+		ToAddress: toAddr1.String(),
+		Status:    types.OUT_FLOW_STATUS_ACTIVE,
+		Rate:      toAddr1Rate,
+	}
+	toAddr2 := sample.RandAccAddress()
+	toAddr2Rate := math.NewInt(10)
+	outFlow2 := types.OutFlow{
+		ToAddress: toAddr2.String(),
+		Status:    types.OUT_FLOW_STATUS_ACTIVE,
+		Rate:      toAddr2Rate,
+	}
+
+	// add new flows
+	count := keeper.MergeActiveOutFlows(ctx, addr, []types.OutFlow{outFlow1, outFlow2})
+	require.Equal(t, 2, count)
+
+	outFlows := keeper.GetOutFlows(ctx, addr)
+	require.Equal(t, 2, len(outFlows))
+
+	// update existing flows
+	count = keeper.MergeActiveOutFlows(ctx, addr, []types.OutFlow{outFlow1, outFlow2})
+	require.Equal(t, 0, count)
+
+	outFlows = keeper.GetOutFlows(ctx, addr)
+	require.Equal(t, 2, len(outFlows))
+
+	// add new flow
+	outFlow3 := types.OutFlow{
+		ToAddress: sample.RandAccAddress().String(),
+		Status:    types.OUT_FLOW_STATUS_ACTIVE,
+		Rate:      toAddr2Rate,
+	}
+
+	count = keeper.MergeActiveOutFlows(ctx, addr, []types.OutFlow{outFlow3})
+	require.Equal(t, 1, count)
+
+	outFlows = keeper.GetOutFlows(ctx, addr)
+	require.Equal(t, 3, len(outFlows))
+
+	// delete a flow
+	outFlow4 := types.OutFlow{
+		ToAddress: toAddr1.String(),
+		Status:    types.OUT_FLOW_STATUS_ACTIVE,
+		Rate:      toAddr1Rate.MulRaw(2).Neg(),
+	}
+
+	count = keeper.MergeActiveOutFlows(ctx, addr, []types.OutFlow{outFlow4})
+	require.Equal(t, -1, count)
+
+	outFlows = keeper.GetOutFlows(ctx, addr)
+	require.Equal(t, 2, len(outFlows))
+}
+
+func TestGetOutFlows(t *testing.T) {
+	keeper, ctx, _ := makePaymentKeeper(t)
+
+	addr1 := sample.RandAccAddress()
+	toAddr1 := sample.RandAccAddress()
+	toAddr1Rate := math.NewInt(10)
+	outFlow1 := types.OutFlow{
+		ToAddress: toAddr1.String(),
+		Status:    types.OUT_FLOW_STATUS_ACTIVE,
+		Rate:      toAddr1Rate,
+	}
+	keeper.SetOutFlow(ctx, addr1, &outFlow1)
+
+	addr2 := sample.RandAccAddress()
+	toAddr2 := sample.RandAccAddress()
+	toAddr2Rate := math.NewInt(10)
+	outFlow2 := types.OutFlow{
+		ToAddress: toAddr2.String(),
+		Status:    types.OUT_FLOW_STATUS_ACTIVE,
+		Rate:      toAddr2Rate,
+	}
+	keeper.SetOutFlow(ctx, addr2, &outFlow2)
+
+	require.Equal(t, 1, len(keeper.GetOutFlows(ctx, addr1)))
+	require.Equal(t, 1, len(keeper.GetOutFlows(ctx, addr2)))
+}
diff --git a/x/payment/keeper/price.go b/x/payment/keeper/price.go
index 7205ee18f..294525b83 100644
--- a/x/payment/keeper/price.go
+++ b/x/payment/keeper/price.go
@@ -9,13 +9,9 @@ import (
 )
 
 func (k Keeper) GetStoragePrice(ctx sdk.Context, params types.StoragePriceParams) (price types.StoragePrice, err error) {
-	primarySp, err := sdk.AccAddressFromHexUnsafe(params.PrimarySp)
+	primarySpPrice, err := k.spKeeper.GetSpStoragePriceByTime(ctx, params.PrimarySp, params.PriceTime)
 	if err != nil {
-		return types.StoragePrice{}, fmt.Errorf("invalid primary sp address: %s", params.PrimarySp)
-	}
-	primarySpPrice, err := k.spKeeper.GetSpStoragePriceByTime(ctx, primarySp, params.PriceTime)
-	if err != nil {
-		return types.StoragePrice{}, fmt.Errorf("get sp [%s] storage price @[%d] failed: %w", params.PrimarySp, params.PriceTime, err)
+		return types.StoragePrice{}, fmt.Errorf("get sp %d storage price @%d failed: %w", params.PrimarySp, params.PriceTime, err)
 	}
 	secondarySpStorePrice, err := k.spKeeper.GetSecondarySpStorePriceByTime(ctx, params.PriceTime)
 	if err != nil {
diff --git a/x/payment/keeper/storage_fee_charge.go b/x/payment/keeper/storage_fee_charge.go
index 3ced8d9f8..da215a57e 100644
--- a/x/payment/keeper/storage_fee_charge.go
+++ b/x/payment/keeper/storage_fee_charge.go
@@ -65,8 +65,6 @@ func (k Keeper) ApplyUserFlowsList(ctx sdk.Context, userFlowsList []types.UserFl
 			streamRecordChanges = append(streamRecordChanges, *types.NewDefaultStreamRecordChangeWithAddr(sdk.MustAccAddressFromHex(flowChange.ToAddress)).WithRateChange(flowChange.Rate))
 			totalRate = totalRate.Add(flowChange.Rate)
 		}
-		// update flows
-		streamRecord.OutFlows = k.MergeOutFlows(append(streamRecord.OutFlows, userFlows.Flows...))
 		streamRecordChange := types.NewDefaultStreamRecordChangeWithAddr(from).WithRateChange(totalRate.Neg())
 		// storage fee preview
 		if ctx.IsCheckTx() {
@@ -83,10 +81,15 @@ func (k Keeper) ApplyUserFlowsList(ctx sdk.Context, userFlowsList []types.UserFl
 			}
 			_ = ctx.EventManager().EmitTypedEvents(event)
 		}
-		err = k.UpdateStreamRecord(ctx, streamRecord, streamRecordChange, false)
+		err = k.UpdateStreamRecord(ctx, streamRecord, streamRecordChange)
 		if err != nil {
 			return fmt.Errorf("apply stream record changes for user failed: %w", err)
 		}
+
+		// update flows
+		deltaFlowCount := k.MergeActiveOutFlows(ctx, from, userFlows.Flows) // deltaFlowCount can be negative
+		streamRecord.OutFlowCount = uint64(int64(streamRecord.OutFlowCount) + int64(deltaFlowCount))
+
 		k.SetStreamRecord(ctx, streamRecord)
 	}
 	err = k.ApplyStreamRecordChanges(ctx, streamRecordChanges)
diff --git a/x/payment/keeper/storage_fee_charge_test.go b/x/payment/keeper/storage_fee_charge_test.go
index 4b62da6c7..13d2a85b4 100644
--- a/x/payment/keeper/storage_fee_charge_test.go
+++ b/x/payment/keeper/storage_fee_charge_test.go
@@ -126,8 +126,9 @@ func TestAutoForceSettle(t *testing.T) {
 	userStreamRecord, found = keeper.GetStreamRecord(ctx, user)
 	t.Logf("user stream record: %+v", userStreamRecord)
 	require.True(t, found)
-	require.Equal(t, 1, len(userStreamRecord.OutFlows))
-	require.Equal(t, userStreamRecord.OutFlows[0].ToAddress, sp.String())
+	outFlows := keeper.GetOutFlows(ctx, user)
+	require.Equal(t, 1, len(outFlows))
+	require.Equal(t, outFlows[0].ToAddress, sp.String())
 	spStreamRecord, found := keeper.GetStreamRecord(ctx, sp)
 	t.Logf("sp stream record: %+v", spStreamRecord)
 	require.True(t, found)
@@ -160,15 +161,14 @@ func TestAutoForceSettle(t *testing.T) {
 	autoSettleQueue2 := keeper.GetAllAutoSettleRecord(ctx)
 	t.Logf("auto settle queue: %+v", autoSettleQueue2)
 	require.Equal(t, autoSettleQueue[0].Timestamp+100, autoSettleQueue2[0].Timestamp)
-	// reverve time - forced settle time - 1 day + 101s pass
+	// reserve time - forced settle time - 1 day + 101s pass
 	ctx = ctx.WithBlockTime(ctx.BlockTime().Add(time.Duration(params.VersionedParams.ReserveTime-params.ForcedSettleTime-86400+101) * time.Second))
-	change = types.NewDefaultStreamRecordChangeWithAddr(user)
 	usrBeforeForceSettle, _ := keeper.GetStreamRecord(ctx, user)
 	t.Logf("usrBeforeForceSettle: %s", usrBeforeForceSettle)
-	usr, _ := keeper.GetStreamRecord(ctx, user)
-	err = keeper.UpdateStreamRecord(ctx, usr, change, true)
-	require.NoError(t, err)
-	keeper.SetStreamRecord(ctx, usr)
+
+	time.Sleep(1 * time.Second)
+	keeper.AutoSettle(ctx)
+
 	usrAfterForceSettle, found := keeper.GetStreamRecord(ctx, user)
 	require.True(t, found)
 	t.Logf("usrAfterForceSettle: %s", usrAfterForceSettle)
diff --git a/x/payment/keeper/stream_record.go b/x/payment/keeper/stream_record.go
index 5a3ccadfb..5765af0d4 100644
--- a/x/payment/keeper/stream_record.go
+++ b/x/payment/keeper/stream_record.go
@@ -47,15 +47,15 @@ func (k Keeper) SetStreamRecord(ctx sdk.Context, streamRecord *types.StreamRecor
 	// set the field back, the streamRecord may be used after this function
 	streamRecord.Account = account
 	event := &types.EventStreamRecordUpdate{
-		Account:         streamRecord.Account,
-		StaticBalance:   streamRecord.StaticBalance,
-		NetflowRate:     streamRecord.NetflowRate,
-		CrudTimestamp:   streamRecord.CrudTimestamp,
-		Status:          streamRecord.Status,
-		LockBalance:     streamRecord.LockBalance,
-		BufferBalance:   streamRecord.BufferBalance,
-		SettleTimestamp: streamRecord.SettleTimestamp,
-		OutFlows:        streamRecord.OutFlows,
+		Account:           streamRecord.Account,
+		StaticBalance:     streamRecord.StaticBalance,
+		NetflowRate:       streamRecord.NetflowRate,
+		FrozenNetflowRate: streamRecord.FrozenNetflowRate,
+		CrudTimestamp:     streamRecord.CrudTimestamp,
+		Status:            streamRecord.Status,
+		LockBalance:       streamRecord.LockBalance,
+		BufferBalance:     streamRecord.BufferBalance,
+		SettleTimestamp:   streamRecord.SettleTimestamp,
 	}
 	_ = ctx.EventManager().EmitTypedEvents(event)
 }
@@ -79,11 +79,18 @@ func (k Keeper) GetStreamRecord(
 	return val, true
 }
 
+func (k Keeper) IsEmptyNetFlow(ctx sdk.Context, account sdk.AccAddress) bool {
+	record, found := k.GetStreamRecord(ctx, account)
+	if !found {
+		return true // treat as empty, for internal use only
+	}
+	return record.NetflowRate.IsZero() && record.FrozenNetflowRate.IsZero()
+}
+
 // GetAllStreamRecord returns all streamRecord
 func (k Keeper) GetAllStreamRecord(ctx sdk.Context) (list []types.StreamRecord) {
 	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.StreamRecordKeyPrefix)
 	iterator := storetypes.KVStorePrefixIterator(store, []byte{})
-
 	defer iterator.Close()
 
 	for ; iterator.Valid(); iterator.Next() {
@@ -100,9 +107,6 @@ func (k Keeper) GetAllStreamRecord(ctx sdk.Context) (list []types.StreamRecord)
 // it only handles the lock balance change and ignore the other changes(since the streams are already changed and the
 // accumulated OutFlows are changed outside this function)
 func (k Keeper) UpdateFrozenStreamRecord(ctx sdk.Context, streamRecord *types.StreamRecord, change *types.StreamRecordChange) error {
-	if streamRecord.Status != types.STREAM_ACCOUNT_STATUS_FROZEN {
-		return fmt.Errorf("stream account %s is not frozen", streamRecord.Account)
-	}
 	currentTimestamp := ctx.BlockTime().Unix()
 	streamRecord.CrudTimestamp = currentTimestamp
 	// update lock balance
@@ -116,15 +120,15 @@ func (k Keeper) UpdateFrozenStreamRecord(ctx sdk.Context, streamRecord *types.St
 	return nil
 }
 
-func (k Keeper) UpdateStreamRecord(ctx sdk.Context, streamRecord *types.StreamRecord, change *types.StreamRecordChange, autoSettle bool) error {
+func (k Keeper) UpdateStreamRecord(ctx sdk.Context, streamRecord *types.StreamRecord, change *types.StreamRecordChange) error {
+	forced, _ := ctx.Value(types.ForceUpdateStreamRecordKey).(bool) // force update in end block
 	if streamRecord.Status != types.STREAM_ACCOUNT_STATUS_ACTIVE {
-		if streamRecord.Status == types.STREAM_ACCOUNT_STATUS_FROZEN {
-			if forced, ok := ctx.Value(types.ForceUpdateFrozenStreamRecordKey).(bool); forced && ok {
-				return k.UpdateFrozenStreamRecord(ctx, streamRecord, change)
-			}
+		if forced { //stream record is frozen
+			return k.UpdateFrozenStreamRecord(ctx, streamRecord, change)
 		}
 		return fmt.Errorf("stream account %s is frozen", streamRecord.Account)
 	}
+
 	isPay := change.StaticBalanceChange.IsNegative() || change.RateChange.IsNegative()
 	currentTimestamp := ctx.BlockTime().Unix()
 	timestamp := streamRecord.CrudTimestamp
@@ -175,33 +179,72 @@ func (k Keeper) UpdateStreamRecord(ctx sdk.Context, streamRecord *types.StreamRe
 		}
 	}
 	// if the change is a pay(which decreases the static balance or netflow rate), the left static balance should be enough
-	if isPay && streamRecord.StaticBalance.IsNegative() {
-		return fmt.Errorf("stream account %s balance not enough, lack of %s BNB wei", streamRecord.Account, streamRecord.StaticBalance.Abs())
+	if !forced && isPay && streamRecord.StaticBalance.IsNegative() {
+		return fmt.Errorf("stream account %s balance not enough, lack of %s BNB", streamRecord.Account, streamRecord.StaticBalance.Abs())
 	}
 	//calculate settle time
 	var settleTimestamp int64 = 0
 	if streamRecord.NetflowRate.IsNegative() {
 		payDuration := streamRecord.StaticBalance.Add(streamRecord.BufferBalance).Quo(streamRecord.NetflowRate.Abs())
 		if payDuration.LTE(sdkmath.NewIntFromUint64(params.ForcedSettleTime)) {
-			if !autoSettle {
+			if !forced {
 				return fmt.Errorf("stream account %s balance not enough, lack of %s BNB", streamRecord.Account, streamRecord.StaticBalance.Abs())
 			}
+		}
+		settleTimestamp = currentTimestamp - int64(params.ForcedSettleTime) + payDuration.Int64()
+	}
+	k.UpdateAutoSettleRecord(ctx, sdk.MustAccAddressFromHex(streamRecord.Account), streamRecord.SettleTimestamp, settleTimestamp)
+	streamRecord.SettleTimestamp = settleTimestamp
+	return nil
+}
+
+func (k Keeper) SettleStreamRecord(ctx sdk.Context, streamRecord *types.StreamRecord) error {
+	currentTimestamp := ctx.BlockTime().Unix()
+	crudTimestamp := streamRecord.CrudTimestamp
+	params := k.GetParams(ctx)
+
+	if currentTimestamp != crudTimestamp {
+		if !streamRecord.NetflowRate.IsZero() {
+			flowDelta := streamRecord.NetflowRate.MulRaw(currentTimestamp - crudTimestamp)
+			streamRecord.StaticBalance = streamRecord.StaticBalance.Add(flowDelta)
+		}
+		streamRecord.CrudTimestamp = currentTimestamp
+	}
+
+	if streamRecord.StaticBalance.IsNegative() {
+		account := sdk.MustAccAddressFromHex(streamRecord.Account)
+		hasBankAccount := k.accountKeeper.HasAccount(ctx, account)
+		if hasBankAccount {
+			coins := sdk.NewCoins(sdk.NewCoin(params.FeeDenom, streamRecord.StaticBalance.Abs()))
+			err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, account, types.ModuleName, coins)
+			if err != nil {
+				ctx.Logger().Info("auto transfer failed when settling", "account", streamRecord.Account, "err", err, "coins", coins)
+			} else {
+				streamRecord.StaticBalance = sdkmath.ZeroInt()
+			}
+		}
+	}
+
+	if streamRecord.NetflowRate.IsNegative() {
+		payDuration := streamRecord.StaticBalance.Add(streamRecord.BufferBalance).Quo(streamRecord.NetflowRate.Abs())
+		if payDuration.LTE(sdkmath.NewIntFromUint64(params.ForcedSettleTime)) {
 			err := k.ForceSettle(ctx, streamRecord)
 			if err != nil {
-				return fmt.Errorf("check and force settle failed, err: %w", err)
+				return err
 			}
 		} else {
-			settleTimestamp = currentTimestamp - int64(params.ForcedSettleTime) + payDuration.Int64()
+			settleTimestamp := currentTimestamp - int64(params.ForcedSettleTime) + payDuration.Int64()
+			k.UpdateAutoSettleRecord(ctx, sdk.MustAccAddressFromHex(streamRecord.Account), streamRecord.SettleTimestamp, settleTimestamp)
+			streamRecord.SettleTimestamp = settleTimestamp
 		}
 	}
-	k.UpdateAutoSettleRecord(ctx, sdk.MustAccAddressFromHex(streamRecord.Account), streamRecord.SettleTimestamp, settleTimestamp)
-	streamRecord.SettleTimestamp = settleTimestamp
+
 	return nil
 }
 
 func (k Keeper) UpdateStreamRecordByAddr(ctx sdk.Context, change *types.StreamRecordChange) (ret *types.StreamRecord, err error) {
 	streamRecord, _ := k.GetStreamRecord(ctx, change.Addr)
-	err = k.UpdateStreamRecord(ctx, streamRecord, change, false)
+	err = k.UpdateStreamRecord(ctx, streamRecord, change)
 	if err != nil {
 		return
 	}
@@ -211,28 +254,17 @@ func (k Keeper) UpdateStreamRecordByAddr(ctx sdk.Context, change *types.StreamRe
 
 func (k Keeper) ForceSettle(ctx sdk.Context, streamRecord *types.StreamRecord) error {
 	totalBalance := streamRecord.StaticBalance.Add(streamRecord.BufferBalance)
-	change := types.NewDefaultStreamRecordChangeWithAddr(types.GovernanceAddress).WithStaticBalanceChange(totalBalance)
-	_, err := k.UpdateStreamRecordByAddr(ctx, change)
-	if err != nil {
-		return fmt.Errorf("update governance stream record failed: %w", err)
+	if totalBalance.IsPositive() {
+		change := types.NewDefaultStreamRecordChangeWithAddr(types.GovernanceAddress).WithStaticBalanceChange(totalBalance)
+		_, err := k.UpdateStreamRecordByAddr(ctx, change)
+		if err != nil {
+			return fmt.Errorf("update governance stream record failed: %w", err)
+		}
 	}
 	// force settle
 	streamRecord.StaticBalance = sdkmath.ZeroInt()
 	streamRecord.BufferBalance = sdkmath.ZeroInt()
-	streamRecord.NetflowRate = sdkmath.ZeroInt()
 	streamRecord.Status = types.STREAM_ACCOUNT_STATUS_FROZEN
-	// todo: use a cache for SP stream record update to optimize
-	// the implementation itself may cause chain force settle, but in reality, it will not happen.
-	// only the SP can be the flow receiver, so in settlement, the rate of SP will reduce, but never get below zero and
-	// trigger another force settle.
-	for _, flow := range streamRecord.OutFlows {
-		toAddr := sdk.MustAccAddressFromHex(flow.ToAddress)
-		flowChange := types.NewDefaultStreamRecordChangeWithAddr(toAddr).WithRateChange(flow.Rate.Neg())
-		_, err = k.UpdateStreamRecordByAddr(ctx, flowChange)
-		if err != nil {
-			return fmt.Errorf("update receiver stream record failed: %w", err)
-		}
-	}
 	// emit event
 	_ = ctx.EventManager().EmitTypedEvents(&types.EventForceSettle{
 		Addr:           streamRecord.Account,
@@ -245,72 +277,260 @@ func (k Keeper) AutoSettle(ctx sdk.Context) {
 	currentTimestamp := ctx.BlockTime().Unix()
 	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.AutoSettleRecordKeyPrefix)
 	iterator := storetypes.KVStorePrefixIterator(store, []byte{})
-
 	defer iterator.Close()
 
-	var num uint64 = 0
-	maxNum := k.GetParams(ctx).MaxAutoForceSettleNum
+	count := uint64(0)
+	max := k.GetParams(ctx).MaxAutoSettleFlowCount
 	for ; iterator.Valid(); iterator.Next() {
-		if num >= maxNum {
+		if count >= max {
 			return
 		}
-		val := types.ParseAutoSettleRecordKey(iterator.Key())
-		addr := sdk.MustAccAddressFromHex(val.Addr)
-		if val.Timestamp > currentTimestamp {
+		record := types.ParseAutoSettleRecordKey(iterator.Key())
+		addr := sdk.MustAccAddressFromHex(record.Addr)
+		if record.Timestamp > currentTimestamp {
 			return
 		}
 		streamRecord, found := k.GetStreamRecord(ctx, addr)
-		if !found {
-			ctx.Logger().Error("stream record not found", "addr", val.Addr)
-			panic("stream record not found")
+		if !found { // should not happen
+			ctx.Logger().Error("auto settle, stream record not found", "address", record.Addr)
+			continue
 		}
-		change := types.NewDefaultStreamRecordChangeWithAddr(addr)
-		err := k.UpdateStreamRecord(ctx, streamRecord, change, true)
-		if err != nil {
-			ctx.Logger().Error("force settle failed", "addr", val.Addr, "err", err)
-			panic("force settle failed")
+
+		if streamRecord.Status == types.STREAM_ACCOUNT_STATUS_ACTIVE {
+			count++ // add one for a stream record
+			err := k.SettleStreamRecord(ctx, streamRecord)
+			if err != nil {
+				ctx.Logger().Error("auto settle, settle stream record failed", "err", err.Error())
+				continue
+			}
+			k.SetStreamRecord(ctx, streamRecord)
+			if streamRecord.Status == types.STREAM_ACCOUNT_STATUS_ACTIVE {
+				continue
+			}
+			if count >= max {
+				return
+			}
 		}
+
+		if k.ExistsAutoResumeRecord(ctx, record.Timestamp, addr) { // this check should be cheap usually
+			continue //skip the one if the stream account is in resuming
+		}
+
+		activeFlowKey := types.OutFlowKey(addr, types.OUT_FLOW_STATUS_ACTIVE, nil)
+		flowStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.OutFlowKeyPrefix)
+		flowIterator := flowStore.Iterator(activeFlowKey, nil)
+		defer flowIterator.Close()
+
+		finished := false
+		totalRate := sdk.ZeroInt()
+		toUpdate := make([]types.OutFlow, 0)
+		for ; flowIterator.Valid(); flowIterator.Next() {
+			if count >= max {
+				break
+			}
+			addrInKey, outFlow := types.ParseOutFlowKey(flowIterator.Key())
+			if !addrInKey.Equals(addr) {
+				finished = true
+				break
+			}
+			if outFlow.Status == types.OUT_FLOW_STATUS_FROZEN {
+				finished = true
+				break
+			}
+			outFlow.Rate = types.ParseOutFlowValue(flowIterator.Value())
+			ctx.Logger().Debug("auto settling record", "height", ctx.BlockHeight(),
+				"address", addr.String(),
+				"to address", outFlow.ToAddress,
+				"rate", outFlow.Rate.String())
+
+			toAddr := sdk.MustAccAddressFromHex(outFlow.ToAddress)
+			flowChange := types.NewDefaultStreamRecordChangeWithAddr(toAddr).WithRateChange(outFlow.Rate.Neg())
+			_, err := k.UpdateStreamRecordByAddr(ctx, flowChange)
+			if err != nil {
+				ctx.Logger().Error("auto settle, update stream record failed", "address", outFlow.ToAddress, "rate", outFlow.Rate.Neg())
+			}
+
+			flowStore.Delete(flowIterator.Key())
+
+			outFlow.Status = types.OUT_FLOW_STATUS_FROZEN
+			toUpdate = append(toUpdate, outFlow)
+
+			totalRate = totalRate.Add(outFlow.Rate)
+			count++
+		}
+
+		for _, outFlow := range toUpdate {
+			k.SetOutFlow(ctx, addr, &outFlow)
+		}
+
+		streamRecord.NetflowRate = streamRecord.NetflowRate.Add(totalRate)
+		streamRecord.FrozenNetflowRate = streamRecord.FrozenNetflowRate.Add(totalRate.Neg())
+
+		if !flowIterator.Valid() || finished {
+			if !streamRecord.NetflowRate.IsZero() {
+				ctx.Logger().Error("should not happen, stream netflow rate is not zero", "address", streamRecord.Account)
+			}
+			k.RemoveAutoSettleRecord(ctx, record.Timestamp, addr)
+		}
+
 		k.SetStreamRecord(ctx, streamRecord)
-		num += 1
 	}
-
 }
 
 func (k Keeper) TryResumeStreamRecord(ctx sdk.Context, streamRecord *types.StreamRecord, depositBalance sdkmath.Int) error {
 	if streamRecord.Status != types.STREAM_ACCOUNT_STATUS_FROZEN {
 		return fmt.Errorf("stream account %s status is not frozen", streamRecord.Account)
 	}
-	streamRecord.StaticBalance = streamRecord.StaticBalance.Add(depositBalance)
+
+	if !streamRecord.NetflowRate.IsNil() && !streamRecord.NetflowRate.IsZero() { // the account is resuming
+		return fmt.Errorf("stream account %s status is resuming, although it is frozen now", streamRecord.Account)
+	}
+
 	params := k.GetParams(ctx)
 	reserveTime := params.VersionedParams.ReserveTime
 	forcedSettleTime := params.ForcedSettleTime
-	totalRates := sdkmath.ZeroInt()
-	for _, flow := range streamRecord.OutFlows {
-		totalRates = totalRates.Add(flow.Rate)
-	}
-	expectedBalanceToResume := totalRates.Mul(sdkmath.NewIntFromUint64(reserveTime))
+
+	totalRate := streamRecord.NetflowRate.Add(streamRecord.FrozenNetflowRate)
+	streamRecord.StaticBalance = streamRecord.StaticBalance.Add(depositBalance)
+	expectedBalanceToResume := totalRate.Neg().Mul(sdkmath.NewIntFromUint64(reserveTime))
 	if streamRecord.StaticBalance.LT(expectedBalanceToResume) {
 		// deposit balance is not enough to resume, only add static balance
 		k.SetStreamRecord(ctx, streamRecord)
 		return nil
 	}
-	// resume
+
 	now := ctx.BlockTime().Unix()
-	streamRecord.Status = types.STREAM_ACCOUNT_STATUS_ACTIVE
-	streamRecord.SettleTimestamp = now + streamRecord.StaticBalance.Quo(totalRates).Int64() - int64(forcedSettleTime)
-	streamRecord.NetflowRate = totalRates.Neg()
+	streamRecord.SettleTimestamp = now + streamRecord.StaticBalance.Quo(totalRate).Int64() - int64(forcedSettleTime)
 	streamRecord.BufferBalance = expectedBalanceToResume
 	streamRecord.StaticBalance = streamRecord.StaticBalance.Sub(expectedBalanceToResume)
 	streamRecord.CrudTimestamp = now
-	for _, flow := range streamRecord.OutFlows {
-		toAddr := sdk.MustAccAddressFromHex(flow.ToAddress)
-		change := types.NewDefaultStreamRecordChangeWithAddr(toAddr).WithRateChange(flow.Rate)
-		_, err := k.UpdateStreamRecordByAddr(ctx, change)
-		if err != nil {
-			return fmt.Errorf("update receiver stream record failed: %w", err)
+
+	ctx.Logger().Debug("try to resume stream account", "streamRecord.OutFlowCount", streamRecord.OutFlowCount, "params.MaxAutoResumeFlowCount", params.MaxAutoResumeFlowCount)
+	if streamRecord.OutFlowCount <= params.MaxAutoResumeFlowCount { //only rough judgement, resume directly
+		streamRecord.Status = types.STREAM_ACCOUNT_STATUS_ACTIVE
+		streamRecord.NetflowRate = totalRate
+		streamRecord.FrozenNetflowRate = sdkmath.ZeroInt()
+
+		addr := sdk.MustAccAddressFromHex(streamRecord.Account)
+		frozenFlowKey := types.OutFlowKey(addr, types.OUT_FLOW_STATUS_FROZEN, nil)
+		flowStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.OutFlowKeyPrefix)
+		flowIterator := flowStore.Iterator(frozenFlowKey, nil)
+		defer flowIterator.Close()
+
+		toUpdate := make([]types.OutFlow, 0)
+		for ; flowIterator.Valid(); flowIterator.Next() {
+			addrInKey, outFlow := types.ParseOutFlowKey(flowIterator.Key())
+			if !addrInKey.Equals(addr) {
+				break
+			}
+
+			outFlow.Rate = types.ParseOutFlowValue(flowIterator.Value())
+
+			toAddr := sdk.MustAccAddressFromHex(outFlow.ToAddress)
+			change := types.NewDefaultStreamRecordChangeWithAddr(toAddr).WithRateChange(outFlow.Rate)
+			_, err := k.UpdateStreamRecordByAddr(ctx, change)
+			if err != nil {
+				return fmt.Errorf("try resume, update receiver stream record failed: %w", err)
+			}
+
+			flowStore.Delete(flowIterator.Key())
+
+			outFlow.Status = types.OUT_FLOW_STATUS_ACTIVE
+			toUpdate = append(toUpdate, outFlow)
+		}
+		for _, outFlow := range toUpdate {
+			k.SetOutFlow(ctx, addr, &outFlow)
+		}
+
+		k.SetStreamRecord(ctx, streamRecord)
+		k.UpdateAutoSettleRecord(ctx, sdk.MustAccAddressFromHex(streamRecord.Account), 0, streamRecord.SettleTimestamp)
+		return nil
+	} else { //enqueue for resume in end block
+		k.SetStreamRecord(ctx, streamRecord)
+		k.SetAutoResumeRecord(ctx, &types.AutoResumeRecord{
+			Timestamp: now,
+			Addr:      streamRecord.Account,
+		})
+		return nil
+	}
+}
+
+func (k Keeper) AutoResume(ctx sdk.Context) {
+	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.AutoResumeRecordKeyPrefix)
+	iterator := storetypes.KVStorePrefixIterator(store, []byte{})
+	defer iterator.Close()
+
+	var count uint64 = 0
+	max := k.GetParams(ctx).MaxAutoResumeFlowCount
+	for ; iterator.Valid(); iterator.Next() {
+		record := types.ParseAutoResumeRecordKey(iterator.Key())
+		addr := sdk.MustAccAddressFromHex(record.Addr)
+
+		streamRecord, found := k.GetStreamRecord(ctx, addr)
+		if !found { // should not happen
+			ctx.Logger().Error("auto resume, stream record not found", "address", record.Addr)
+			continue
+		}
+
+		totalRate := sdk.ZeroInt()
+		frozenFlowKey := types.OutFlowKey(addr, types.OUT_FLOW_STATUS_FROZEN, nil)
+		flowStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.OutFlowKeyPrefix)
+		flowIterator := flowStore.Iterator(frozenFlowKey, nil)
+		defer flowIterator.Close()
+
+		finished := false
+		toUpdate := make([]types.OutFlow, 0)
+		for ; flowIterator.Valid(); flowIterator.Next() {
+			if count >= max {
+				break
+			}
+			addrInKey, outFlow := types.ParseOutFlowKey(flowIterator.Key())
+			if !addrInKey.Equals(addr) {
+				finished = true
+				break
+			}
+
+			outFlow.Rate = types.ParseOutFlowValue(flowIterator.Value())
+			ctx.Logger().Debug("auto resuming record", "height", ctx.BlockHeight(),
+				"address", addr.String(),
+				"to address", outFlow.ToAddress,
+				"rate", outFlow.Rate.String())
+
+			toAddr := sdk.MustAccAddressFromHex(outFlow.ToAddress)
+			flowChange := types.NewDefaultStreamRecordChangeWithAddr(toAddr).WithRateChange(outFlow.Rate)
+			_, err := k.UpdateStreamRecordByAddr(ctx, flowChange)
+			if err != nil {
+				ctx.Logger().Error("auto resume, update receiver stream record failed", "address", outFlow.ToAddress, "err", err.Error())
+				break
+			}
+
+			flowStore.Delete(flowIterator.Key())
+
+			outFlow.Status = types.OUT_FLOW_STATUS_ACTIVE
+			toUpdate = append(toUpdate, outFlow)
+
+			totalRate = totalRate.Add(outFlow.Rate)
+			count++
+		}
+
+		for _, outFlow := range toUpdate {
+			k.SetOutFlow(ctx, addr, &outFlow)
 		}
+
+		streamRecord.NetflowRate = streamRecord.NetflowRate.Add(totalRate.Neg())
+		streamRecord.FrozenNetflowRate = streamRecord.FrozenNetflowRate.Add(totalRate)
+		if !flowIterator.Valid() || finished {
+			if !streamRecord.FrozenNetflowRate.IsZero() {
+				ctx.Logger().Error("should not happen, stream frozen netflow rate is not zero", "address", streamRecord.Account)
+			}
+			streamRecord.Status = types.STREAM_ACCOUNT_STATUS_ACTIVE
+			change := types.NewDefaultStreamRecordChangeWithAddr(addr)
+			err := k.UpdateStreamRecord(ctx, streamRecord, change)
+			if err != nil {
+				ctx.Logger().Error("auto resume, update  stream record failed", "err", err.Error())
+			}
+			k.RemoveAutoResumeRecord(ctx, record.Timestamp, addr)
+		}
+		k.SetStreamRecord(ctx, streamRecord)
 	}
-	k.SetStreamRecord(ctx, streamRecord)
-	k.UpdateAutoSettleRecord(ctx, sdk.MustAccAddressFromHex(streamRecord.Account), 0, streamRecord.SettleTimestamp)
-	return nil
 }
diff --git a/x/payment/keeper/stream_record_test.go b/x/payment/keeper/stream_record_test.go
new file mode 100644
index 000000000..5fddae9ae
--- /dev/null
+++ b/x/payment/keeper/stream_record_test.go
@@ -0,0 +1,357 @@
+package keeper_test
+
+import (
+	"errors"
+	"testing"
+	"time"
+
+	sdkmath "cosmossdk.io/math"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/golang/mock/gomock"
+	"github.com/stretchr/testify/require"
+
+	"github.com/bnb-chain/greenfield/testutil/sample"
+	"github.com/bnb-chain/greenfield/x/payment/types"
+)
+
+func TestTryResumeStreamRecord_InResuming(t *testing.T) {
+	keeper, ctx, _ := makePaymentKeeper(t)
+	ctx = ctx.WithBlockTime(time.Now())
+
+	// further deposit to a resuming account is not allowed
+	streamRecord := &types.StreamRecord{
+		Status:      types.STREAM_ACCOUNT_STATUS_FROZEN,
+		NetflowRate: sdkmath.NewInt(-100),
+	}
+	deposit := sdkmath.NewInt(100)
+	err := keeper.TryResumeStreamRecord(ctx, streamRecord, deposit)
+	require.ErrorContains(t, err, "resuming")
+}
+
+func TestTryResumeStreamRecord_ResumeInOneBlock(t *testing.T) {
+	keeper, ctx, _ := makePaymentKeeper(t)
+	ctx = ctx.WithBlockTime(time.Now())
+
+	// resume account in one call
+	params := keeper.GetParams(ctx)
+	rate := sdkmath.NewInt(100)
+	user := sample.RandAccAddress()
+	streamRecord := &types.StreamRecord{
+		StaticBalance:     sdkmath.ZeroInt(),
+		BufferBalance:     sdkmath.ZeroInt(),
+		LockBalance:       sdkmath.ZeroInt(),
+		Account:           user.String(),
+		Status:            types.STREAM_ACCOUNT_STATUS_FROZEN,
+		NetflowRate:       sdkmath.NewInt(0),
+		FrozenNetflowRate: rate.Neg(),
+		OutFlowCount:      1,
+	}
+	keeper.SetStreamRecord(ctx, streamRecord)
+
+	gvg := sample.RandAccAddress()
+	outFlow := &types.OutFlow{
+		ToAddress: gvg.String(),
+		Rate:      rate,
+		Status:    types.OUT_FLOW_STATUS_FROZEN,
+	}
+	keeper.SetOutFlow(ctx, user, outFlow)
+
+	err := keeper.TryResumeStreamRecord(ctx, streamRecord, rate.MulRaw(int64(params.VersionedParams.ReserveTime)))
+	require.NoError(t, err)
+
+	userStreamRecord, _ := keeper.GetStreamRecord(ctx, user)
+	require.True(t, userStreamRecord.Status == types.STREAM_ACCOUNT_STATUS_ACTIVE)
+	require.Equal(t, userStreamRecord.NetflowRate, rate.Neg())
+	require.Equal(t, userStreamRecord.FrozenNetflowRate, sdkmath.ZeroInt())
+
+	gvgStreamRecord, _ := keeper.GetStreamRecord(ctx, gvg)
+	require.True(t, gvgStreamRecord.Status == types.STREAM_ACCOUNT_STATUS_ACTIVE)
+	require.Equal(t, gvgStreamRecord.NetflowRate, rate)
+	require.Equal(t, gvgStreamRecord.FrozenNetflowRate, sdkmath.ZeroInt())
+}
+
+func TestTryResumeStreamRecord_ResumeInMultipleBlocks(t *testing.T) {
+	keeper, ctx, _ := makePaymentKeeper(t)
+	ctx = ctx.WithBlockTime(time.Now())
+
+	// resume account in multiple blocks
+	params := keeper.GetParams(ctx)
+	params.MaxAutoResumeFlowCount = 1
+	_ = keeper.SetParams(ctx, params)
+
+	rate := sdkmath.NewInt(300)
+	user := sample.RandAccAddress()
+	streamRecord := &types.StreamRecord{
+		StaticBalance:     sdkmath.ZeroInt(),
+		BufferBalance:     sdkmath.ZeroInt(),
+		LockBalance:       sdkmath.ZeroInt(),
+		Account:           user.String(),
+		Status:            types.STREAM_ACCOUNT_STATUS_FROZEN,
+		NetflowRate:       sdkmath.NewInt(0),
+		FrozenNetflowRate: rate.Neg(),
+		OutFlowCount:      3,
+	}
+	keeper.SetStreamRecord(ctx, streamRecord)
+
+	gvgAddress := []sdk.AccAddress{sample.RandAccAddress(), sample.RandAccAddress(), sample.RandAccAddress()}
+
+	gvg1 := gvgAddress[0]
+	gvg1Rate := sdk.NewInt(50)
+	outFlow1 := &types.OutFlow{
+		ToAddress: gvg1.String(),
+		Rate:      gvg1Rate,
+		Status:    types.OUT_FLOW_STATUS_FROZEN,
+	}
+	keeper.SetOutFlow(ctx, user, outFlow1)
+
+	gvg2 := gvgAddress[1]
+	gvg2Rate := sdk.NewInt(100)
+	outFlow2 := &types.OutFlow{
+		ToAddress: gvg2.String(),
+		Rate:      gvg2Rate,
+		Status:    types.OUT_FLOW_STATUS_FROZEN,
+	}
+	keeper.SetOutFlow(ctx, user, outFlow2)
+
+	gvg3 := gvgAddress[2]
+	gvg3Rate := sdk.NewInt(150)
+	outFlow3 := &types.OutFlow{
+		ToAddress: gvg3.String(),
+		Rate:      gvg3Rate,
+		Status:    types.OUT_FLOW_STATUS_FROZEN,
+	}
+	keeper.SetOutFlow(ctx, user, outFlow3)
+
+	err := keeper.TryResumeStreamRecord(ctx, streamRecord, rate.MulRaw(int64(params.VersionedParams.ReserveTime)))
+	require.NoError(t, err)
+
+	// still frozen
+	userStreamRecord, _ := keeper.GetStreamRecord(ctx, user)
+	require.True(t, userStreamRecord.Status == types.STREAM_ACCOUNT_STATUS_FROZEN)
+
+	_, found := keeper.GetAutoResumeRecord(ctx, ctx.BlockTime().Unix(), user)
+	require.True(t, found)
+
+	// resume in end block
+	keeper.AutoResume(ctx)
+	userStreamRecord, _ = keeper.GetStreamRecord(ctx, user)
+	require.True(t, userStreamRecord.Status == types.STREAM_ACCOUNT_STATUS_FROZEN)
+
+	// resume in end block
+	keeper.AutoResume(ctx)
+	userStreamRecord, _ = keeper.GetStreamRecord(ctx, user)
+	require.True(t, userStreamRecord.Status == types.STREAM_ACCOUNT_STATUS_FROZEN)
+
+	// resume in end block
+	keeper.AutoResume(ctx)
+	userStreamRecord, _ = keeper.GetStreamRecord(ctx, user)
+	require.True(t, userStreamRecord.Status == types.STREAM_ACCOUNT_STATUS_ACTIVE)
+	require.Equal(t, userStreamRecord.NetflowRate, rate.Neg())
+	require.Equal(t, userStreamRecord.FrozenNetflowRate, sdkmath.ZeroInt())
+
+	gvg1StreamRecord, _ := keeper.GetStreamRecord(ctx, gvg1)
+	require.True(t, gvg1StreamRecord.Status == types.STREAM_ACCOUNT_STATUS_ACTIVE)
+	require.Equal(t, gvg1StreamRecord.NetflowRate, gvg1Rate)
+	require.Equal(t, gvg1StreamRecord.FrozenNetflowRate, sdkmath.ZeroInt())
+
+	gvg2StreamRecord, _ := keeper.GetStreamRecord(ctx, gvg2)
+	require.True(t, gvg2StreamRecord.Status == types.STREAM_ACCOUNT_STATUS_ACTIVE)
+	require.Equal(t, gvg2StreamRecord.NetflowRate, gvg2Rate)
+	require.Equal(t, gvg2StreamRecord.FrozenNetflowRate, sdkmath.ZeroInt())
+
+	gvg3StreamRecord, _ := keeper.GetStreamRecord(ctx, gvg3)
+	require.True(t, gvg3StreamRecord.Status == types.STREAM_ACCOUNT_STATUS_ACTIVE)
+	require.Equal(t, gvg3StreamRecord.NetflowRate, gvg3Rate)
+	require.Equal(t, gvg3StreamRecord.FrozenNetflowRate, sdkmath.ZeroInt())
+}
+
+func TestAutoSettle_FreezeInOneBlock(t *testing.T) {
+	keeper, ctx, depKeepers := makePaymentKeeper(t)
+	ctx = ctx.WithBlockTime(time.Now())
+
+	depKeepers.AccountKeeper.EXPECT().HasAccount(gomock.Any(), gomock.Any()).
+		Return(true).AnyTimes()
+	depKeepers.BankKeeper.EXPECT().SendCoinsFromAccountToModule(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
+		Return(errors.New("fail to transfer")).AnyTimes()
+
+	// freeze account in one block
+	rate := sdkmath.NewInt(100)
+	user := sample.RandAccAddress()
+	userStreamRecord := &types.StreamRecord{
+		StaticBalance:     sdkmath.ZeroInt(),
+		BufferBalance:     sdkmath.ZeroInt(),
+		LockBalance:       sdkmath.ZeroInt(),
+		Account:           user.String(),
+		Status:            types.STREAM_ACCOUNT_STATUS_ACTIVE,
+		NetflowRate:       rate.Neg(),
+		FrozenNetflowRate: sdkmath.NewInt(0),
+		OutFlowCount:      1,
+	}
+	keeper.SetStreamRecord(ctx, userStreamRecord)
+
+	gvg := sample.RandAccAddress()
+	gvgStreamRecord := &types.StreamRecord{
+		StaticBalance:     sdkmath.ZeroInt(),
+		BufferBalance:     sdkmath.ZeroInt(),
+		LockBalance:       sdkmath.ZeroInt(),
+		Account:           gvg.String(),
+		Status:            types.STREAM_ACCOUNT_STATUS_ACTIVE,
+		NetflowRate:       sdkmath.NewInt(0),
+		FrozenNetflowRate: sdkmath.NewInt(0),
+		OutFlowCount:      0,
+	}
+	keeper.SetStreamRecord(ctx, gvgStreamRecord)
+
+	outFlow := &types.OutFlow{
+		ToAddress: gvg.String(),
+		Rate:      rate,
+		Status:    types.OUT_FLOW_STATUS_ACTIVE,
+	}
+	keeper.SetOutFlow(ctx, user, outFlow)
+
+	keeper.SetAutoSettleRecord(ctx, &types.AutoSettleRecord{
+		Timestamp: ctx.BlockTime().Unix(),
+		Addr:      user.String(),
+	})
+
+	keeper.AutoSettle(ctx)
+
+	userStreamRecord, _ = keeper.GetStreamRecord(ctx, user)
+	require.True(t, userStreamRecord.Status == types.STREAM_ACCOUNT_STATUS_FROZEN)
+	require.Equal(t, userStreamRecord.NetflowRate, sdkmath.ZeroInt())
+	require.Equal(t, userStreamRecord.FrozenNetflowRate, rate.Neg())
+
+	gvgOutFlow := keeper.GetOutFlow(ctx, user, types.OUT_FLOW_STATUS_FROZEN, gvg)
+	require.Equal(t, gvgOutFlow.Status, types.OUT_FLOW_STATUS_FROZEN)
+	require.Equal(t, gvgOutFlow.Rate, rate)
+}
+
+func TestAutoSettle_FreezeInMultipleBlocks(t *testing.T) {
+	keeper, ctx, depKeepers := makePaymentKeeper(t)
+	ctx = ctx.WithBlockTime(time.Now())
+
+	// freeze account in multiple blocks
+	params := keeper.GetParams(ctx)
+	params.MaxAutoSettleFlowCount = 1
+	_ = keeper.SetParams(ctx, params)
+
+	depKeepers.AccountKeeper.EXPECT().HasAccount(gomock.Any(), gomock.Any()).
+		Return(true).AnyTimes()
+	depKeepers.BankKeeper.EXPECT().SendCoinsFromAccountToModule(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
+		Return(errors.New("fail to transfer")).AnyTimes()
+
+	rate := sdkmath.NewInt(300)
+	user := sample.RandAccAddress()
+	userStreamRecord := &types.StreamRecord{
+		StaticBalance:     sdkmath.ZeroInt(),
+		BufferBalance:     sdkmath.ZeroInt(),
+		LockBalance:       sdkmath.ZeroInt(),
+		Account:           user.String(),
+		Status:            types.STREAM_ACCOUNT_STATUS_ACTIVE,
+		NetflowRate:       rate.Neg(),
+		FrozenNetflowRate: sdkmath.NewInt(0),
+		OutFlowCount:      3,
+	}
+	keeper.SetStreamRecord(ctx, userStreamRecord)
+
+	gvgAddress := []sdk.AccAddress{sample.RandAccAddress(), sample.RandAccAddress(), sample.RandAccAddress()}
+
+	gvg1 := gvgAddress[0]
+	gvg1StreamRecord := &types.StreamRecord{
+		StaticBalance:     sdkmath.ZeroInt(),
+		BufferBalance:     sdkmath.ZeroInt(),
+		LockBalance:       sdkmath.ZeroInt(),
+		Account:           gvg1.String(),
+		Status:            types.STREAM_ACCOUNT_STATUS_ACTIVE,
+		NetflowRate:       sdkmath.NewInt(0),
+		FrozenNetflowRate: sdkmath.NewInt(0),
+		OutFlowCount:      0,
+	}
+	keeper.SetStreamRecord(ctx, gvg1StreamRecord)
+
+	outFlow1 := &types.OutFlow{
+		ToAddress: gvg1.String(),
+		Rate:      sdkmath.NewInt(50),
+		Status:    types.OUT_FLOW_STATUS_ACTIVE,
+	}
+	keeper.SetOutFlow(ctx, user, outFlow1)
+
+	gvg2 := gvgAddress[1]
+	gvg2StreamRecord := &types.StreamRecord{
+		StaticBalance:     sdkmath.ZeroInt(),
+		BufferBalance:     sdkmath.ZeroInt(),
+		LockBalance:       sdkmath.ZeroInt(),
+		Account:           gvg2.String(),
+		Status:            types.STREAM_ACCOUNT_STATUS_ACTIVE,
+		NetflowRate:       sdkmath.NewInt(0),
+		FrozenNetflowRate: sdkmath.NewInt(0),
+		OutFlowCount:      0,
+	}
+	keeper.SetStreamRecord(ctx, gvg2StreamRecord)
+
+	outFlow2 := &types.OutFlow{
+		ToAddress: gvg2.String(),
+		Rate:      sdkmath.NewInt(100),
+		Status:    types.OUT_FLOW_STATUS_ACTIVE,
+	}
+	keeper.SetOutFlow(ctx, user, outFlow2)
+
+	gvg3 := gvgAddress[2]
+	gvg3StreamRecord := &types.StreamRecord{
+		StaticBalance:     sdkmath.ZeroInt(),
+		BufferBalance:     sdkmath.ZeroInt(),
+		LockBalance:       sdkmath.ZeroInt(),
+		Account:           gvg3.String(),
+		Status:            types.STREAM_ACCOUNT_STATUS_ACTIVE,
+		NetflowRate:       sdkmath.NewInt(0),
+		FrozenNetflowRate: sdkmath.NewInt(0),
+		OutFlowCount:      0,
+	}
+	keeper.SetStreamRecord(ctx, gvg3StreamRecord)
+
+	outFlow3 := &types.OutFlow{
+		ToAddress: gvg3.String(),
+		Rate:      sdkmath.NewInt(150),
+		Status:    types.OUT_FLOW_STATUS_ACTIVE,
+	}
+	keeper.SetOutFlow(ctx, user, outFlow3)
+
+	keeper.SetAutoSettleRecord(ctx, &types.AutoSettleRecord{
+		Timestamp: ctx.BlockTime().Unix(),
+		Addr:      user.String(),
+	})
+
+	keeper.AutoSettle(ctx) // this is for settle stream, it is counted
+	userStreamRecord, _ = keeper.GetStreamRecord(ctx, user)
+	require.True(t, userStreamRecord.Status == types.STREAM_ACCOUNT_STATUS_FROZEN)
+
+	keeper.AutoSettle(ctx)
+	userStreamRecord, _ = keeper.GetStreamRecord(ctx, user)
+	require.True(t, userStreamRecord.Status == types.STREAM_ACCOUNT_STATUS_FROZEN)
+	require.True(t, !userStreamRecord.NetflowRate.IsZero())
+	require.True(t, !userStreamRecord.FrozenNetflowRate.IsZero())
+
+	keeper.AutoSettle(ctx)
+	userStreamRecord, _ = keeper.GetStreamRecord(ctx, user)
+	require.True(t, userStreamRecord.Status == types.STREAM_ACCOUNT_STATUS_FROZEN)
+	require.True(t, !userStreamRecord.NetflowRate.IsZero())
+	require.True(t, !userStreamRecord.FrozenNetflowRate.IsZero())
+
+	keeper.AutoSettle(ctx)
+	userStreamRecord, _ = keeper.GetStreamRecord(ctx, user)
+	require.True(t, userStreamRecord.Status == types.STREAM_ACCOUNT_STATUS_FROZEN)
+	require.True(t, userStreamRecord.NetflowRate.IsZero())
+	require.True(t, userStreamRecord.FrozenNetflowRate.Equal(rate.Neg()))
+
+	gvg1OutFlow := keeper.GetOutFlow(ctx, user, types.OUT_FLOW_STATUS_FROZEN, gvg1)
+	require.Equal(t, gvg1OutFlow.Status, types.OUT_FLOW_STATUS_FROZEN)
+	require.Equal(t, gvg1OutFlow.Rate, sdkmath.NewInt(50))
+
+	gvg2OutFlow := keeper.GetOutFlow(ctx, user, types.OUT_FLOW_STATUS_FROZEN, gvg2)
+	require.Equal(t, gvg2OutFlow.Status, types.OUT_FLOW_STATUS_FROZEN)
+	require.Equal(t, gvg2OutFlow.Rate, sdkmath.NewInt(100))
+
+	gvg3OutFlow := keeper.GetOutFlow(ctx, user, types.OUT_FLOW_STATUS_FROZEN, gvg3)
+	require.Equal(t, gvg3OutFlow.Status, types.OUT_FLOW_STATUS_FROZEN)
+	require.Equal(t, gvg3OutFlow.Rate, sdkmath.NewInt(150))
+}
diff --git a/x/payment/module.go b/x/payment/module.go
index bb8eb912d..228832500 100644
--- a/x/payment/module.go
+++ b/x/payment/module.go
@@ -145,6 +145,7 @@ func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {}
 
 // EndBlock contains the logic that is automatically triggered at the end of each block
 func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate {
+	am.keeper.AutoResume(ctx)
 	am.keeper.AutoSettle(ctx)
 	return []abci.ValidatorUpdate{}
 }
diff --git a/x/payment/types/auto_resume_record.pb.go b/x/payment/types/auto_resume_record.pb.go
new file mode 100644
index 000000000..90d406c2d
--- /dev/null
+++ b/x/payment/types/auto_resume_record.pb.go
@@ -0,0 +1,362 @@
+// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// source: greenfield/payment/auto_resume_record.proto
+
+package types
+
+import (
+	fmt "fmt"
+	_ "github.com/cosmos/cosmos-proto"
+	proto "github.com/cosmos/gogoproto/proto"
+	io "io"
+	math "math"
+	math_bits "math/bits"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
+
+// AutoResumeRecord is the record keeps the auto resume information.
+// The EndBlocker of payment module will scan the list of AutoResumeRecord
+// and resume the stream account one by one.
+type AutoResumeRecord struct {
+	// timestamp is the unix timestamp to order the records
+	Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+	// the stream account address
+	Addr string `protobuf:"bytes,2,opt,name=addr,proto3" json:"addr,omitempty"`
+}
+
+func (m *AutoResumeRecord) Reset()         { *m = AutoResumeRecord{} }
+func (m *AutoResumeRecord) String() string { return proto.CompactTextString(m) }
+func (*AutoResumeRecord) ProtoMessage()    {}
+func (*AutoResumeRecord) Descriptor() ([]byte, []int) {
+	return fileDescriptor_462258ebd1137873, []int{0}
+}
+func (m *AutoResumeRecord) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *AutoResumeRecord) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_AutoResumeRecord.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *AutoResumeRecord) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_AutoResumeRecord.Merge(m, src)
+}
+func (m *AutoResumeRecord) XXX_Size() int {
+	return m.Size()
+}
+func (m *AutoResumeRecord) XXX_DiscardUnknown() {
+	xxx_messageInfo_AutoResumeRecord.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_AutoResumeRecord proto.InternalMessageInfo
+
+func (m *AutoResumeRecord) GetTimestamp() int64 {
+	if m != nil {
+		return m.Timestamp
+	}
+	return 0
+}
+
+func (m *AutoResumeRecord) GetAddr() string {
+	if m != nil {
+		return m.Addr
+	}
+	return ""
+}
+
+func init() {
+	proto.RegisterType((*AutoResumeRecord)(nil), "greenfield.payment.AutoResumeRecord")
+}
+
+func init() {
+	proto.RegisterFile("greenfield/payment/auto_resume_record.proto", fileDescriptor_462258ebd1137873)
+}
+
+var fileDescriptor_462258ebd1137873 = []byte{
+	// 233 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x4e, 0x2f, 0x4a, 0x4d,
+	0xcd, 0x4b, 0xcb, 0x4c, 0xcd, 0x49, 0xd1, 0x2f, 0x48, 0xac, 0xcc, 0x4d, 0xcd, 0x2b, 0xd1, 0x4f,
+	0x2c, 0x2d, 0xc9, 0x8f, 0x2f, 0x4a, 0x2d, 0x2e, 0xcd, 0x4d, 0x8d, 0x2f, 0x4a, 0x4d, 0xce, 0x2f,
+	0x4a, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x42, 0x28, 0xd6, 0x83, 0x2a, 0x96, 0x92,
+	0x4c, 0xce, 0x2f, 0xce, 0xcd, 0x2f, 0x8e, 0x07, 0xab, 0xd0, 0x87, 0x70, 0x20, 0xca, 0x95, 0xe2,
+	0xb8, 0x04, 0x1c, 0x4b, 0x4b, 0xf2, 0x83, 0xc0, 0x26, 0x05, 0x81, 0x0d, 0x12, 0x92, 0xe1, 0xe2,
+	0x2c, 0xc9, 0xcc, 0x4d, 0x2d, 0x2e, 0x49, 0xcc, 0x2d, 0x90, 0x60, 0x54, 0x60, 0xd4, 0x60, 0x0e,
+	0x42, 0x08, 0x08, 0xe9, 0x70, 0xb1, 0x24, 0xa6, 0xa4, 0x14, 0x49, 0x30, 0x29, 0x30, 0x6a, 0x70,
+	0x3a, 0x49, 0x5c, 0xda, 0xa2, 0x2b, 0x02, 0x35, 0xd1, 0x31, 0x25, 0xa5, 0x28, 0xb5, 0xb8, 0x38,
+	0xb8, 0xa4, 0x28, 0x33, 0x2f, 0x3d, 0x08, 0xac, 0xca, 0xc9, 0xf3, 0xc4, 0x23, 0x39, 0xc6, 0x0b,
+	0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, 0xe1, 0xc2, 0x63, 0x39, 0x86,
+	0x1b, 0x8f, 0xe5, 0x18, 0xa2, 0xf4, 0xd3, 0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73,
+	0xf5, 0x93, 0xf2, 0x92, 0x74, 0x93, 0x33, 0x12, 0x33, 0xf3, 0xf4, 0x91, 0xbc, 0x5a, 0x01, 0xf7,
+	0x6c, 0x49, 0x65, 0x41, 0x6a, 0x71, 0x12, 0x1b, 0xd8, 0xc5, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff,
+	0xff, 0x59, 0xff, 0x94, 0x6e, 0x0f, 0x01, 0x00, 0x00,
+}
+
+func (m *AutoResumeRecord) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *AutoResumeRecord) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *AutoResumeRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.Addr) > 0 {
+		i -= len(m.Addr)
+		copy(dAtA[i:], m.Addr)
+		i = encodeVarintAutoResumeRecord(dAtA, i, uint64(len(m.Addr)))
+		i--
+		dAtA[i] = 0x12
+	}
+	if m.Timestamp != 0 {
+		i = encodeVarintAutoResumeRecord(dAtA, i, uint64(m.Timestamp))
+		i--
+		dAtA[i] = 0x8
+	}
+	return len(dAtA) - i, nil
+}
+
+func encodeVarintAutoResumeRecord(dAtA []byte, offset int, v uint64) int {
+	offset -= sovAutoResumeRecord(v)
+	base := offset
+	for v >= 1<<7 {
+		dAtA[offset] = uint8(v&0x7f | 0x80)
+		v >>= 7
+		offset++
+	}
+	dAtA[offset] = uint8(v)
+	return base
+}
+func (m *AutoResumeRecord) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.Timestamp != 0 {
+		n += 1 + sovAutoResumeRecord(uint64(m.Timestamp))
+	}
+	l = len(m.Addr)
+	if l > 0 {
+		n += 1 + l + sovAutoResumeRecord(uint64(l))
+	}
+	return n
+}
+
+func sovAutoResumeRecord(x uint64) (n int) {
+	return (math_bits.Len64(x|1) + 6) / 7
+}
+func sozAutoResumeRecord(x uint64) (n int) {
+	return sovAutoResumeRecord(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *AutoResumeRecord) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowAutoResumeRecord
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: AutoResumeRecord: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: AutoResumeRecord: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType)
+			}
+			m.Timestamp = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowAutoResumeRecord
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.Timestamp |= int64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Addr", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowAutoResumeRecord
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthAutoResumeRecord
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthAutoResumeRecord
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Addr = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipAutoResumeRecord(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthAutoResumeRecord
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func skipAutoResumeRecord(dAtA []byte) (n int, err error) {
+	l := len(dAtA)
+	iNdEx := 0
+	depth := 0
+	for iNdEx < l {
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return 0, ErrIntOverflowAutoResumeRecord
+			}
+			if iNdEx >= l {
+				return 0, io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		wireType := int(wire & 0x7)
+		switch wireType {
+		case 0:
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowAutoResumeRecord
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				iNdEx++
+				if dAtA[iNdEx-1] < 0x80 {
+					break
+				}
+			}
+		case 1:
+			iNdEx += 8
+		case 2:
+			var length int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowAutoResumeRecord
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				length |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if length < 0 {
+				return 0, ErrInvalidLengthAutoResumeRecord
+			}
+			iNdEx += length
+		case 3:
+			depth++
+		case 4:
+			if depth == 0 {
+				return 0, ErrUnexpectedEndOfGroupAutoResumeRecord
+			}
+			depth--
+		case 5:
+			iNdEx += 4
+		default:
+			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+		}
+		if iNdEx < 0 {
+			return 0, ErrInvalidLengthAutoResumeRecord
+		}
+		if depth == 0 {
+			return iNdEx, nil
+		}
+	}
+	return 0, io.ErrUnexpectedEOF
+}
+
+var (
+	ErrInvalidLengthAutoResumeRecord        = fmt.Errorf("proto: negative length found during unmarshaling")
+	ErrIntOverflowAutoResumeRecord          = fmt.Errorf("proto: integer overflow")
+	ErrUnexpectedEndOfGroupAutoResumeRecord = fmt.Errorf("proto: unexpected end of group")
+)
diff --git a/x/payment/types/events.pb.go b/x/payment/types/events.pb.go
index 27e47ab5c..2f6aac479 100644
--- a/x/payment/types/events.pb.go
+++ b/x/payment/types/events.pb.go
@@ -122,19 +122,19 @@ type EventStreamRecordUpdate struct {
 	// The per-second rate that an account's balance is changing.
 	// It is the sum of the account's inbound and outbound flow rates.
 	NetflowRate github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,3,opt,name=netflow_rate,json=netflowRate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"netflow_rate"`
+	// The frozen netflow rate, which is used when resuming stream account
+	FrozenNetflowRate github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,4,opt,name=frozen_netflow_rate,json=frozenNetflowRate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"frozen_netflow_rate"`
 	// The balance of the stream account at the latest CRUD timestamp.
-	StaticBalance github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,4,opt,name=static_balance,json=staticBalance,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"static_balance"`
+	StaticBalance github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,5,opt,name=static_balance,json=staticBalance,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"static_balance"`
 	// reserved balance of the stream account
 	// If the netflow rate is negative, the reserved balance is `netflow_rate * reserve_time`
-	BufferBalance github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,5,opt,name=buffer_balance,json=bufferBalance,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"buffer_balance"`
+	BufferBalance github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,6,opt,name=buffer_balance,json=bufferBalance,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"buffer_balance"`
 	// the locked balance of the stream account after it puts a new object and before the object is sealed
-	LockBalance github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,6,opt,name=lock_balance,json=lockBalance,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"lock_balance"`
+	LockBalance github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,7,opt,name=lock_balance,json=lockBalance,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"lock_balance"`
 	// the status of the stream account
-	Status StreamAccountStatus `protobuf:"varint,7,opt,name=status,proto3,enum=greenfield.payment.StreamAccountStatus" json:"status,omitempty"`
+	Status StreamAccountStatus `protobuf:"varint,8,opt,name=status,proto3,enum=greenfield.payment.StreamAccountStatus" json:"status,omitempty"`
 	// the unix timestamp when the stream account will be settled
-	SettleTimestamp int64 `protobuf:"varint,8,opt,name=settle_timestamp,json=settleTimestamp,proto3" json:"settle_timestamp,omitempty"`
-	// the accumulated outflow rates of the stream account
-	OutFlows []OutFlow `protobuf:"bytes,9,rep,name=out_flows,json=outFlows,proto3" json:"out_flows"`
+	SettleTimestamp int64 `protobuf:"varint,9,opt,name=settle_timestamp,json=settleTimestamp,proto3" json:"settle_timestamp,omitempty"`
 }
 
 func (m *EventStreamRecordUpdate) Reset()         { *m = EventStreamRecordUpdate{} }
@@ -198,13 +198,6 @@ func (m *EventStreamRecordUpdate) GetSettleTimestamp() int64 {
 	return 0
 }
 
-func (m *EventStreamRecordUpdate) GetOutFlows() []OutFlow {
-	if m != nil {
-		return m.OutFlows
-	}
-	return nil
-}
-
 // EventForceSettle may be emitted on all Msgs and EndBlocker when a payment account's
 // balance or net outflow rate is changed
 type EventForceSettle struct {
@@ -436,54 +429,53 @@ func init() {
 func init() { proto.RegisterFile("greenfield/payment/events.proto", fileDescriptor_befcc80e27bc8df9) }
 
 var fileDescriptor_befcc80e27bc8df9 = []byte{
-	// 738 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x95, 0xcf, 0x4f, 0xe3, 0x46,
-	0x14, 0xc7, 0xe3, 0x10, 0x02, 0x19, 0x20, 0x44, 0x16, 0x52, 0x03, 0x15, 0x26, 0x8d, 0x54, 0x9a,
-	0x56, 0x4d, 0x22, 0xa5, 0xd7, 0xaa, 0x15, 0x29, 0x8e, 0x14, 0x15, 0x95, 0xc8, 0x09, 0x45, 0xad,
-	0x54, 0x59, 0xfe, 0xf1, 0x1c, 0x2c, 0x62, 0x8f, 0x35, 0x33, 0x26, 0xe5, 0x3f, 0xe8, 0xb1, 0xd7,
-	0x3d, 0xef, 0x61, 0x2f, 0x7b, 0xe4, 0xb8, 0x7b, 0xe7, 0x88, 0x38, 0xad, 0xf6, 0x80, 0x56, 0x70,
-	0xda, 0xff, 0x62, 0xe5, 0x99, 0x49, 0x08, 0x22, 0x52, 0xd8, 0x55, 0x38, 0x25, 0xf3, 0xfc, 0xcd,
-	0xfb, 0x7e, 0xde, 0xbc, 0xf7, 0x62, 0xb4, 0xd3, 0x27, 0x00, 0xa1, 0xe7, 0xc3, 0xc0, 0xad, 0x47,
-	0xd6, 0x79, 0x00, 0x21, 0xab, 0xc3, 0x19, 0x84, 0x8c, 0xd6, 0x22, 0x82, 0x19, 0x56, 0xd5, 0x7b,
-	0x41, 0x4d, 0x0a, 0xb6, 0x36, 0x1d, 0x4c, 0x03, 0x4c, 0x4d, 0xae, 0xa8, 0x8b, 0x83, 0x90, 0x6f,
-	0x6d, 0xf4, 0x71, 0x1f, 0x8b, 0x78, 0xf2, 0x4d, 0x46, 0xb7, 0xa7, 0xb8, 0xd8, 0x16, 0x05, 0xf9,
-	0x78, 0x77, 0xca, 0x63, 0xca, 0x08, 0x58, 0x81, 0x49, 0xc0, 0xc1, 0xc4, 0x15, 0xba, 0xf2, 0x0b,
-	0x05, 0x6d, 0xea, 0x09, 0x5c, 0x47, 0x88, 0xf6, 0x1c, 0x07, 0xc7, 0x21, 0x3b, 0x8a, 0x5c, 0x8b,
-	0x81, 0xfa, 0x23, 0xca, 0x58, 0xae, 0x4b, 0x8a, 0x4a, 0x49, 0xa9, 0xe4, 0x9a, 0xc5, 0xeb, 0x8b,
-	0xea, 0x86, 0x44, 0xdb, 0x73, 0x5d, 0x02, 0x94, 0x76, 0x19, 0xf1, 0xc3, 0xbe, 0xc1, 0x55, 0x6a,
-	0x0d, 0x2d, 0xe2, 0x61, 0x08, 0xa4, 0x98, 0x9e, 0x21, 0x17, 0x32, 0x55, 0x43, 0x88, 0x80, 0x17,
-	0x87, 0xae, 0x65, 0x0f, 0xa0, 0xb8, 0x50, 0x52, 0x2a, 0xcb, 0xc6, 0x44, 0xa4, 0xfc, 0x7a, 0x11,
-	0x7d, 0xc5, 0xd9, 0xba, 0x1c, 0xdc, 0xe0, 0xdc, 0x92, 0xac, 0x81, 0x96, 0x2c, 0x81, 0x3a, 0x13,
-	0x6e, 0x24, 0x54, 0xbf, 0x45, 0x79, 0x87, 0xc4, 0xae, 0xc9, 0xfc, 0x00, 0x28, 0xb3, 0x82, 0x88,
-	0x83, 0x2e, 0x18, 0x6b, 0x49, 0xb4, 0x37, 0x0a, 0xaa, 0x26, 0x5a, 0x0d, 0x81, 0x79, 0x03, 0x3c,
-	0x34, 0x89, 0xc5, 0x04, 0x58, 0xae, 0xf9, 0xf3, 0xe5, 0xcd, 0x4e, 0xea, 0xfd, 0xcd, 0xce, 0x6e,
-	0xdf, 0x67, 0x27, 0xb1, 0x5d, 0x73, 0x70, 0x20, 0xdb, 0x24, 0x3f, 0xaa, 0xd4, 0x3d, 0xad, 0xb3,
-	0xf3, 0x08, 0x68, 0xad, 0x1d, 0xb2, 0xeb, 0x8b, 0x2a, 0x92, 0x34, 0xed, 0x90, 0x19, 0x2b, 0x32,
-	0xa3, 0x91, 0xb0, 0x3b, 0x28, 0x4f, 0x99, 0xc5, 0x7c, 0xc7, 0xb4, 0xad, 0x81, 0x15, 0x3a, 0x50,
-	0xcc, 0xcc, 0xc1, 0x62, 0x4d, 0xe4, 0x6c, 0x8a, 0x94, 0x89, 0x89, 0x1d, 0x7b, 0x1e, 0x90, 0xb1,
-	0xc9, 0xe2, 0x3c, 0x4c, 0x44, 0xce, 0x91, 0x89, 0x89, 0x56, 0x07, 0xd8, 0x39, 0x1d, 0x5b, 0x64,
-	0xe7, 0x71, 0x55, 0x49, 0xc6, 0x91, 0xc1, 0xaf, 0x28, 0x9b, 0x94, 0x15, 0xd3, 0xe2, 0x52, 0x49,
-	0xa9, 0xe4, 0x1b, 0xdf, 0xd5, 0x1e, 0xef, 0x4e, 0x4d, 0x8c, 0x87, 0x9c, 0xdc, 0x2e, 0x97, 0x1b,
-	0xf2, 0x67, 0xea, 0xf7, 0xa8, 0x40, 0x81, 0xb1, 0x01, 0x4c, 0x74, 0x7d, 0x99, 0x77, 0x7d, 0x5d,
-	0xc4, 0xef, 0xfb, 0xfe, 0x0b, 0xca, 0xe1, 0x98, 0x99, 0x49, 0x9b, 0x68, 0x31, 0x57, 0x5a, 0xa8,
-	0xac, 0x34, 0xbe, 0x9e, 0x66, 0x77, 0x18, 0xb3, 0xd6, 0x00, 0x0f, 0x9b, 0x99, 0xa4, 0x4c, 0x63,
-	0x19, 0x8b, 0x23, 0x2d, 0xbf, 0x52, 0x50, 0x81, 0x8f, 0x6b, 0x0b, 0x13, 0x07, 0xba, 0x3c, 0xfb,
-	0x67, 0x6e, 0x10, 0x20, 0x49, 0xe5, 0x8e, 0xaf, 0x34, 0x3d, 0x87, 0x2b, 0xcd, 0xcb, 0xa4, 0xf2,
-	0x56, 0xcb, 0x6f, 0x14, 0xb4, 0xca, 0x49, 0xf7, 0x21, 0xc2, 0xd4, 0x67, 0x09, 0xa5, 0x47, 0x70,
-	0x30, 0x9b, 0x32, 0x51, 0xa9, 0x15, 0x94, 0x66, 0x78, 0xe6, 0x92, 0xa7, 0x19, 0x56, 0x7b, 0x28,
-	0x6b, 0x05, 0x7c, 0x49, 0xe7, 0xb1, 0x44, 0x32, 0x57, 0xf9, 0xad, 0x82, 0xd6, 0x38, 0xfe, 0xb1,
-	0xcf, 0x4e, 0x5c, 0x62, 0x0d, 0x25, 0x91, 0xf2, 0x04, 0xa2, 0x51, 0xa5, 0xe9, 0x27, 0x55, 0xfa,
-	0x3c, 0xfc, 0x1f, 0x15, 0xb4, 0x2e, 0x06, 0x05, 0xa0, 0x43, 0xe0, 0xcc, 0x87, 0xe1, 0x17, 0xfd,
-	0x9f, 0x1d, 0xa0, 0x82, 0x07, 0x60, 0x46, 0x22, 0x85, 0x99, 0xd8, 0xf2, 0xba, 0xf2, 0x8d, 0xf2,
-	0xb4, 0xb9, 0xbd, 0x77, 0xeb, 0x9d, 0x47, 0x60, 0xe4, 0xbd, 0x07, 0xe7, 0xe7, 0xa9, 0xf5, 0x87,
-	0x7f, 0x50, 0xfe, 0xa1, 0xaf, 0x5a, 0x46, 0x5a, 0x4b, 0xd7, 0xcd, 0x8e, 0xa1, 0xff, 0xd9, 0xd6,
-	0x8f, 0xcd, 0xde, 0x5f, 0x1d, 0x7e, 0x38, 0x38, 0xfc, 0xed, 0x77, 0x7d, 0xdf, 0x6c, 0xe9, 0x7a,
-	0x21, 0xa5, 0x7e, 0x83, 0xb6, 0x1f, 0x69, 0x8e, 0xfe, 0x98, 0x90, 0x28, 0x5b, 0x99, 0xff, 0x5e,
-	0x6a, 0xa9, 0x66, 0xfb, 0xf2, 0x56, 0x53, 0xae, 0x6e, 0x35, 0xe5, 0xc3, 0xad, 0xa6, 0xfc, 0x7f,
-	0xa7, 0xa5, 0xae, 0xee, 0xb4, 0xd4, 0xbb, 0x3b, 0x2d, 0xf5, 0x77, 0x7d, 0x02, 0xdb, 0x0e, 0xed,
-	0xaa, 0x73, 0x62, 0xf9, 0x61, 0x7d, 0xe2, 0xad, 0xf8, 0xef, 0xf8, 0xbd, 0xc8, 0x6b, 0xb0, 0xb3,
-	0xfc, 0x85, 0xf8, 0xd3, 0xa7, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd4, 0xb7, 0x1e, 0xdc, 0xbf, 0x07,
-	0x00, 0x00,
+	// 724 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x95, 0xc1, 0x4e, 0xdb, 0x4a,
+	0x14, 0x86, 0xe3, 0x00, 0x01, 0xe6, 0x42, 0xc8, 0xf5, 0x45, 0xba, 0x01, 0xe9, 0x1a, 0x88, 0x74,
+	0x69, 0x5a, 0x35, 0x89, 0x44, 0xb7, 0x95, 0x2a, 0x52, 0x8c, 0x14, 0x15, 0xd1, 0xc8, 0x09, 0x45,
+	0xad, 0x54, 0x59, 0x13, 0xfb, 0x38, 0x58, 0xc4, 0x33, 0xd6, 0x78, 0x4c, 0x4a, 0x9f, 0xa0, 0xcb,
+	0x6e, 0xbb, 0xee, 0xa2, 0x2f, 0xc0, 0xb2, 0xdd, 0xb3, 0x44, 0xac, 0x2a, 0x16, 0xa8, 0x82, 0x55,
+	0xdf, 0xa2, 0xf2, 0xcc, 0x24, 0x04, 0x81, 0x14, 0x5a, 0x85, 0x55, 0x32, 0x27, 0x7f, 0xfe, 0xff,
+	0x3b, 0xe3, 0x33, 0x1e, 0xb4, 0xd4, 0x66, 0x00, 0xc4, 0xf3, 0xa1, 0xe3, 0x56, 0x42, 0x7c, 0x18,
+	0x00, 0xe1, 0x15, 0x38, 0x00, 0xc2, 0xa3, 0x72, 0xc8, 0x28, 0xa7, 0xba, 0x7e, 0x25, 0x28, 0x2b,
+	0xc1, 0xe2, 0x82, 0x43, 0xa3, 0x80, 0x46, 0xb6, 0x50, 0x54, 0xe4, 0x42, 0xca, 0x17, 0xe7, 0xdb,
+	0xb4, 0x4d, 0x65, 0x3d, 0xf9, 0xa6, 0xaa, 0x2b, 0xb7, 0xa4, 0xd0, 0x98, 0xdb, 0x5e, 0x87, 0x76,
+	0x95, 0x64, 0xf5, 0x16, 0x49, 0xc4, 0x19, 0xe0, 0xc0, 0x66, 0xe0, 0x50, 0xe6, 0x4a, 0x5d, 0xe1,
+	0x93, 0x86, 0x16, 0xcc, 0x04, 0xb0, 0x2e, 0x45, 0xeb, 0x8e, 0x43, 0x63, 0xc2, 0x77, 0x42, 0x17,
+	0x73, 0xd0, 0x1f, 0xa3, 0x71, 0xec, 0xba, 0x2c, 0xaf, 0x2d, 0x6b, 0xc5, 0xe9, 0x6a, 0xfe, 0xf4,
+	0xa8, 0x34, 0xaf, 0xf0, 0xd6, 0x5d, 0x97, 0x41, 0x14, 0x35, 0x38, 0xf3, 0x49, 0xdb, 0x12, 0x2a,
+	0xbd, 0x8c, 0x26, 0x68, 0x97, 0x00, 0xcb, 0xa7, 0x87, 0xc8, 0xa5, 0x4c, 0x37, 0x10, 0x62, 0xe0,
+	0xc5, 0xc4, 0xc5, 0xad, 0x0e, 0xe4, 0xc7, 0x96, 0xb5, 0xe2, 0x94, 0x35, 0x50, 0x29, 0x9c, 0x4d,
+	0xa0, 0x7f, 0x05, 0x5b, 0x43, 0x80, 0x5b, 0x82, 0x5b, 0x91, 0xad, 0xa1, 0x49, 0x2c, 0x51, 0x87,
+	0xc2, 0xf5, 0x84, 0xfa, 0xff, 0x28, 0xeb, 0xb0, 0xd8, 0xb5, 0xb9, 0x1f, 0x40, 0xc4, 0x71, 0x10,
+	0x0a, 0xd0, 0x31, 0x6b, 0x36, 0xa9, 0x36, 0x7b, 0x45, 0xdd, 0x46, 0x33, 0x04, 0x78, 0xb2, 0x97,
+	0x36, 0xc3, 0x5c, 0x82, 0x4d, 0x57, 0x9f, 0x1e, 0x9f, 0x2f, 0xa5, 0xce, 0xce, 0x97, 0x56, 0xdb,
+	0x3e, 0xdf, 0x8b, 0x5b, 0x65, 0x87, 0x06, 0xea, 0x51, 0xa9, 0x8f, 0x52, 0xe4, 0xee, 0x57, 0xf8,
+	0x61, 0x08, 0x51, 0xb9, 0x46, 0xf8, 0xe9, 0x51, 0x09, 0x29, 0x9a, 0x1a, 0xe1, 0xd6, 0x5f, 0xca,
+	0xd1, 0x4a, 0xd8, 0x3b, 0xe8, 0x1f, 0x8f, 0xd1, 0xf7, 0x40, 0xec, 0x6b, 0x39, 0xe3, 0x23, 0xc8,
+	0xf9, 0x5b, 0x1a, 0x6f, 0x0f, 0xa4, 0x39, 0x28, 0x1b, 0x71, 0xcc, 0x7d, 0xc7, 0x6e, 0xe1, 0x0e,
+	0x26, 0x0e, 0xe4, 0x27, 0x46, 0x10, 0x34, 0x2b, 0x3d, 0xab, 0xd2, 0x32, 0x09, 0x69, 0xc5, 0x9e,
+	0x07, 0xac, 0x1f, 0x92, 0x19, 0x45, 0x88, 0xf4, 0xec, 0x85, 0xd8, 0x68, 0xa6, 0x43, 0x9d, 0xfd,
+	0x7e, 0xc4, 0xe4, 0x28, 0x1e, 0x4c, 0xe2, 0xd8, 0x0b, 0x78, 0x86, 0x32, 0x49, 0x5b, 0x71, 0x94,
+	0x9f, 0x5a, 0xd6, 0x8a, 0xd9, 0xb5, 0x07, 0xe5, 0x9b, 0xa7, 0xb5, 0x2c, 0x87, 0x51, 0x9d, 0x93,
+	0x86, 0x90, 0x5b, 0xea, 0x6f, 0xfa, 0x43, 0x94, 0x8b, 0x80, 0xf3, 0x0e, 0x0c, 0xcc, 0xd8, 0xb4,
+	0x98, 0xb1, 0x39, 0x59, 0xef, 0x4f, 0x59, 0xe1, 0x8b, 0x86, 0x72, 0x62, 0xb8, 0x37, 0x29, 0x73,
+	0xa0, 0x21, 0x7e, 0xfd, 0xcd, 0xf3, 0x06, 0x48, 0xb9, 0xba, 0xfd, 0x2d, 0x49, 0x8f, 0x60, 0x4b,
+	0xb2, 0xca, 0x54, 0xed, 0x4a, 0xe1, 0xab, 0x86, 0x66, 0x04, 0xe9, 0x06, 0x84, 0x34, 0xf2, 0x79,
+	0x42, 0xe9, 0x31, 0x1a, 0x0c, 0xa7, 0x4c, 0x54, 0x7a, 0x11, 0xa5, 0x39, 0x1d, 0xfa, 0x4a, 0x48,
+	0x73, 0xaa, 0x37, 0x51, 0x06, 0x07, 0xe2, 0x48, 0x8f, 0xe2, 0xc8, 0x29, 0xaf, 0xc2, 0x37, 0x0d,
+	0xcd, 0x0a, 0xfc, 0x5d, 0x9f, 0xef, 0xb9, 0x0c, 0x77, 0x15, 0x91, 0x76, 0x07, 0xa2, 0x5e, 0xa7,
+	0xe9, 0x3b, 0x75, 0x7a, 0x3f, 0xfc, 0x3f, 0x35, 0x34, 0x27, 0x07, 0x05, 0xa0, 0xce, 0xe0, 0xc0,
+	0x87, 0xee, 0x1f, 0xbd, 0xfd, 0xb6, 0x50, 0xce, 0x03, 0xb0, 0x43, 0x69, 0x61, 0x27, 0xb1, 0xa2,
+	0xaf, 0xec, 0x5a, 0xe1, 0xb6, 0x31, 0xbf, 0x4a, 0x6b, 0x1e, 0x86, 0x60, 0x65, 0xbd, 0x6b, 0xeb,
+	0xfb, 0xe9, 0xf5, 0xd1, 0x5b, 0x94, 0xbd, 0x9e, 0xab, 0x17, 0x90, 0xb1, 0x69, 0x9a, 0x76, 0xdd,
+	0x32, 0x5f, 0xd5, 0xcc, 0x5d, 0xbb, 0xf9, 0xba, 0x2e, 0x16, 0x5b, 0x2f, 0x9f, 0xbf, 0x30, 0x37,
+	0xec, 0x4d, 0xd3, 0xcc, 0xa5, 0xf4, 0x15, 0xf4, 0xdf, 0x0d, 0xcd, 0xce, 0xf6, 0x80, 0x44, 0x5b,
+	0x1c, 0xff, 0xf0, 0xd9, 0x48, 0x55, 0x6b, 0xc7, 0x17, 0x86, 0x76, 0x72, 0x61, 0x68, 0x3f, 0x2e,
+	0x0c, 0xed, 0xe3, 0xa5, 0x91, 0x3a, 0xb9, 0x34, 0x52, 0xdf, 0x2f, 0x8d, 0xd4, 0x9b, 0xca, 0x00,
+	0x76, 0x8b, 0xb4, 0x4a, 0xce, 0x1e, 0xf6, 0x49, 0x65, 0xe0, 0x0e, 0x7d, 0xd7, 0xbf, 0x45, 0x45,
+	0x0f, 0xad, 0x8c, 0xb8, 0x3e, 0x9f, 0xfc, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x68, 0xb8, 0x45, 0x52,
+	0xf1, 0x07, 0x00, 0x00,
 }
 
 func (m *EventPaymentAccountUpdate) Marshal() (dAtA []byte, err error) {
@@ -553,29 +545,15 @@ func (m *EventStreamRecordUpdate) MarshalToSizedBuffer(dAtA []byte) (int, error)
 	_ = i
 	var l int
 	_ = l
-	if len(m.OutFlows) > 0 {
-		for iNdEx := len(m.OutFlows) - 1; iNdEx >= 0; iNdEx-- {
-			{
-				size, err := m.OutFlows[iNdEx].MarshalToSizedBuffer(dAtA[:i])
-				if err != nil {
-					return 0, err
-				}
-				i -= size
-				i = encodeVarintEvents(dAtA, i, uint64(size))
-			}
-			i--
-			dAtA[i] = 0x4a
-		}
-	}
 	if m.SettleTimestamp != 0 {
 		i = encodeVarintEvents(dAtA, i, uint64(m.SettleTimestamp))
 		i--
-		dAtA[i] = 0x40
+		dAtA[i] = 0x48
 	}
 	if m.Status != 0 {
 		i = encodeVarintEvents(dAtA, i, uint64(m.Status))
 		i--
-		dAtA[i] = 0x38
+		dAtA[i] = 0x40
 	}
 	{
 		size := m.LockBalance.Size()
@@ -586,7 +564,7 @@ func (m *EventStreamRecordUpdate) MarshalToSizedBuffer(dAtA []byte) (int, error)
 		i = encodeVarintEvents(dAtA, i, uint64(size))
 	}
 	i--
-	dAtA[i] = 0x32
+	dAtA[i] = 0x3a
 	{
 		size := m.BufferBalance.Size()
 		i -= size
@@ -596,7 +574,7 @@ func (m *EventStreamRecordUpdate) MarshalToSizedBuffer(dAtA []byte) (int, error)
 		i = encodeVarintEvents(dAtA, i, uint64(size))
 	}
 	i--
-	dAtA[i] = 0x2a
+	dAtA[i] = 0x32
 	{
 		size := m.StaticBalance.Size()
 		i -= size
@@ -606,6 +584,16 @@ func (m *EventStreamRecordUpdate) MarshalToSizedBuffer(dAtA []byte) (int, error)
 		i = encodeVarintEvents(dAtA, i, uint64(size))
 	}
 	i--
+	dAtA[i] = 0x2a
+	{
+		size := m.FrozenNetflowRate.Size()
+		i -= size
+		if _, err := m.FrozenNetflowRate.MarshalTo(dAtA[i:]); err != nil {
+			return 0, err
+		}
+		i = encodeVarintEvents(dAtA, i, uint64(size))
+	}
+	i--
 	dAtA[i] = 0x22
 	{
 		size := m.NetflowRate.Size()
@@ -857,6 +845,8 @@ func (m *EventStreamRecordUpdate) Size() (n int) {
 	}
 	l = m.NetflowRate.Size()
 	n += 1 + l + sovEvents(uint64(l))
+	l = m.FrozenNetflowRate.Size()
+	n += 1 + l + sovEvents(uint64(l))
 	l = m.StaticBalance.Size()
 	n += 1 + l + sovEvents(uint64(l))
 	l = m.BufferBalance.Size()
@@ -869,12 +859,6 @@ func (m *EventStreamRecordUpdate) Size() (n int) {
 	if m.SettleTimestamp != 0 {
 		n += 1 + sovEvents(uint64(m.SettleTimestamp))
 	}
-	if len(m.OutFlows) > 0 {
-		for _, e := range m.OutFlows {
-			l = e.Size()
-			n += 1 + l + sovEvents(uint64(l))
-		}
-	}
 	return n
 }
 
@@ -1205,7 +1189,7 @@ func (m *EventStreamRecordUpdate) Unmarshal(dAtA []byte) error {
 			iNdEx = postIndex
 		case 4:
 			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field StaticBalance", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field FrozenNetflowRate", wireType)
 			}
 			var stringLen uint64
 			for shift := uint(0); ; shift += 7 {
@@ -1233,13 +1217,13 @@ func (m *EventStreamRecordUpdate) Unmarshal(dAtA []byte) error {
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			if err := m.StaticBalance.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+			if err := m.FrozenNetflowRate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
 				return err
 			}
 			iNdEx = postIndex
 		case 5:
 			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field BufferBalance", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field StaticBalance", wireType)
 			}
 			var stringLen uint64
 			for shift := uint(0); ; shift += 7 {
@@ -1267,13 +1251,13 @@ func (m *EventStreamRecordUpdate) Unmarshal(dAtA []byte) error {
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			if err := m.BufferBalance.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+			if err := m.StaticBalance.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
 				return err
 			}
 			iNdEx = postIndex
 		case 6:
 			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field LockBalance", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field BufferBalance", wireType)
 			}
 			var stringLen uint64
 			for shift := uint(0); ; shift += 7 {
@@ -1301,15 +1285,15 @@ func (m *EventStreamRecordUpdate) Unmarshal(dAtA []byte) error {
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			if err := m.LockBalance.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+			if err := m.BufferBalance.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
 				return err
 			}
 			iNdEx = postIndex
 		case 7:
-			if wireType != 0 {
-				return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType)
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field LockBalance", wireType)
 			}
-			m.Status = 0
+			var stringLen uint64
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowEvents
@@ -1319,16 +1303,31 @@ func (m *EventStreamRecordUpdate) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				m.Status |= StreamAccountStatus(b&0x7F) << shift
+				stringLen |= uint64(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthEvents
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.LockBalance.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
 		case 8:
 			if wireType != 0 {
-				return fmt.Errorf("proto: wrong wireType = %d for field SettleTimestamp", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType)
 			}
-			m.SettleTimestamp = 0
+			m.Status = 0
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowEvents
@@ -1338,16 +1337,16 @@ func (m *EventStreamRecordUpdate) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				m.SettleTimestamp |= int64(b&0x7F) << shift
+				m.Status |= StreamAccountStatus(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
 		case 9:
-			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field OutFlows", wireType)
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field SettleTimestamp", wireType)
 			}
-			var msglen int
+			m.SettleTimestamp = 0
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowEvents
@@ -1357,26 +1356,11 @@ func (m *EventStreamRecordUpdate) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				msglen |= int(b&0x7F) << shift
+				m.SettleTimestamp |= int64(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
-			if msglen < 0 {
-				return ErrInvalidLengthEvents
-			}
-			postIndex := iNdEx + msglen
-			if postIndex < 0 {
-				return ErrInvalidLengthEvents
-			}
-			if postIndex > l {
-				return io.ErrUnexpectedEOF
-			}
-			m.OutFlows = append(m.OutFlows, OutFlow{})
-			if err := m.OutFlows[len(m.OutFlows)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
-				return err
-			}
-			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipEvents(dAtA[iNdEx:])
diff --git a/x/payment/types/expected_keepers.go b/x/payment/types/expected_keepers.go
index edb4fcb35..943596b10 100644
--- a/x/payment/types/expected_keepers.go
+++ b/x/payment/types/expected_keepers.go
@@ -27,6 +27,6 @@ type BankKeeper interface {
 
 // SpKeeper defines the expected interface needed to retrieve storage provider.
 type SpKeeper interface {
-	GetSpStoragePriceByTime(ctx sdk.Context, spAddr sdk.AccAddress, time int64) (val sptypes.SpStoragePrice, err error)
+	GetSpStoragePriceByTime(ctx sdk.Context, spId uint32, time int64) (val sptypes.SpStoragePrice, err error)
 	GetSecondarySpStorePriceByTime(ctx sdk.Context, time int64) (val sptypes.SecondarySpStorePrice, err error)
 }
diff --git a/x/payment/types/expected_keepers_mocks.go b/x/payment/types/expected_keepers_mocks.go
index 2e6e01a61..fd96f7ab0 100644
--- a/x/payment/types/expected_keepers_mocks.go
+++ b/x/payment/types/expected_keepers_mocks.go
@@ -1,252 +1,251 @@
 // Code generated by MockGen. DO NOT EDIT.
-// Source: ./x/payment/types/expected_keepers.go
+// Source: x/payment/types/expected_keepers.go
 
-// Package mock_types is a generated GoMock package.
+// Package types is a generated GoMock package.
 package types
 
 import (
 	reflect "reflect"
 
+	types "github.com/bnb-chain/greenfield/x/sp/types"
 	types0 "github.com/cosmos/cosmos-sdk/types"
 	types1 "github.com/cosmos/cosmos-sdk/x/auth/types"
 	gomock "github.com/golang/mock/gomock"
-
-	types "github.com/bnb-chain/greenfield/x/sp/types"
 )
 
-// MockAccountKeeper is a mock of AccountKeeper interface
+// MockAccountKeeper is a mock of AccountKeeper interface.
 type MockAccountKeeper struct {
 	ctrl     *gomock.Controller
 	recorder *MockAccountKeeperMockRecorder
 }
 
-// MockAccountKeeperMockRecorder is the mock recorder for MockAccountKeeper
+// MockAccountKeeperMockRecorder is the mock recorder for MockAccountKeeper.
 type MockAccountKeeperMockRecorder struct {
 	mock *MockAccountKeeper
 }
 
-// NewMockAccountKeeper creates a new mock instance
+// NewMockAccountKeeper creates a new mock instance.
 func NewMockAccountKeeper(ctrl *gomock.Controller) *MockAccountKeeper {
 	mock := &MockAccountKeeper{ctrl: ctrl}
 	mock.recorder = &MockAccountKeeperMockRecorder{mock}
 	return mock
 }
 
-// EXPECT returns an object that allows the caller to indicate expected use
+// EXPECT returns an object that allows the caller to indicate expected use.
 func (m *MockAccountKeeper) EXPECT() *MockAccountKeeperMockRecorder {
 	return m.recorder
 }
 
-// GetAccount mocks base method
-func (m *MockAccountKeeper) GetAccount(arg0 types0.Context, arg1 types0.AccAddress) types1.AccountI {
+// GetAccount mocks base method.
+func (m *MockAccountKeeper) GetAccount(ctx types0.Context, addr types0.AccAddress) types1.AccountI {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetAccount", arg0, arg1)
+	ret := m.ctrl.Call(m, "GetAccount", ctx, addr)
 	ret0, _ := ret[0].(types1.AccountI)
 	return ret0
 }
 
-// GetAccount indicates an expected call of GetAccount
-func (mr *MockAccountKeeperMockRecorder) GetAccount(arg0, arg1 interface{}) *gomock.Call {
+// GetAccount indicates an expected call of GetAccount.
+func (mr *MockAccountKeeperMockRecorder) GetAccount(ctx, addr interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetAccount), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetAccount), ctx, addr)
 }
 
-// GetModuleAccount mocks base method
-func (m *MockAccountKeeper) GetModuleAccount(arg0 types0.Context, arg1 string) types1.ModuleAccountI {
+// GetModuleAccount mocks base method.
+func (m *MockAccountKeeper) GetModuleAccount(ctx types0.Context, moduleName string) types1.ModuleAccountI {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetModuleAccount", arg0, arg1)
+	ret := m.ctrl.Call(m, "GetModuleAccount", ctx, moduleName)
 	ret0, _ := ret[0].(types1.ModuleAccountI)
 	return ret0
 }
 
-// GetModuleAccount indicates an expected call of GetModuleAccount
-func (mr *MockAccountKeeperMockRecorder) GetModuleAccount(arg0, arg1 interface{}) *gomock.Call {
+// GetModuleAccount indicates an expected call of GetModuleAccount.
+func (mr *MockAccountKeeperMockRecorder) GetModuleAccount(ctx, moduleName interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetModuleAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetModuleAccount), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetModuleAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetModuleAccount), ctx, moduleName)
 }
 
-// GetModuleAddress mocks base method
-func (m *MockAccountKeeper) GetModuleAddress(arg0 string) types0.AccAddress {
+// GetModuleAddress mocks base method.
+func (m *MockAccountKeeper) GetModuleAddress(name string) types0.AccAddress {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetModuleAddress", arg0)
+	ret := m.ctrl.Call(m, "GetModuleAddress", name)
 	ret0, _ := ret[0].(types0.AccAddress)
 	return ret0
 }
 
-// GetModuleAddress indicates an expected call of GetModuleAddress
-func (mr *MockAccountKeeperMockRecorder) GetModuleAddress(arg0 interface{}) *gomock.Call {
+// GetModuleAddress indicates an expected call of GetModuleAddress.
+func (mr *MockAccountKeeperMockRecorder) GetModuleAddress(name interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetModuleAddress", reflect.TypeOf((*MockAccountKeeper)(nil).GetModuleAddress), arg0)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetModuleAddress", reflect.TypeOf((*MockAccountKeeper)(nil).GetModuleAddress), name)
 }
 
-// HasAccount mocks base method
-func (m *MockAccountKeeper) HasAccount(arg0 types0.Context, arg1 types0.AccAddress) bool {
+// HasAccount mocks base method.
+func (m *MockAccountKeeper) HasAccount(ctx types0.Context, addr types0.AccAddress) bool {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "HasAccount", arg0, arg1)
+	ret := m.ctrl.Call(m, "HasAccount", ctx, addr)
 	ret0, _ := ret[0].(bool)
 	return ret0
 }
 
-// HasAccount indicates an expected call of HasAccount
-func (mr *MockAccountKeeperMockRecorder) HasAccount(arg0, arg1 interface{}) *gomock.Call {
+// HasAccount indicates an expected call of HasAccount.
+func (mr *MockAccountKeeperMockRecorder) HasAccount(ctx, addr interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasAccount", reflect.TypeOf((*MockAccountKeeper)(nil).HasAccount), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasAccount", reflect.TypeOf((*MockAccountKeeper)(nil).HasAccount), ctx, addr)
 }
 
-// SetModuleAccount mocks base method
+// SetModuleAccount mocks base method.
 func (m *MockAccountKeeper) SetModuleAccount(arg0 types0.Context, arg1 types1.ModuleAccountI) {
 	m.ctrl.T.Helper()
 	m.ctrl.Call(m, "SetModuleAccount", arg0, arg1)
 }
 
-// SetModuleAccount indicates an expected call of SetModuleAccount
+// SetModuleAccount indicates an expected call of SetModuleAccount.
 func (mr *MockAccountKeeperMockRecorder) SetModuleAccount(arg0, arg1 interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetModuleAccount", reflect.TypeOf((*MockAccountKeeper)(nil).SetModuleAccount), arg0, arg1)
 }
 
-// MockBankKeeper is a mock of BankKeeper interface
+// MockBankKeeper is a mock of BankKeeper interface.
 type MockBankKeeper struct {
 	ctrl     *gomock.Controller
 	recorder *MockBankKeeperMockRecorder
 }
 
-// MockBankKeeperMockRecorder is the mock recorder for MockBankKeeper
+// MockBankKeeperMockRecorder is the mock recorder for MockBankKeeper.
 type MockBankKeeperMockRecorder struct {
 	mock *MockBankKeeper
 }
 
-// NewMockBankKeeper creates a new mock instance
+// NewMockBankKeeper creates a new mock instance.
 func NewMockBankKeeper(ctrl *gomock.Controller) *MockBankKeeper {
 	mock := &MockBankKeeper{ctrl: ctrl}
 	mock.recorder = &MockBankKeeperMockRecorder{mock}
 	return mock
 }
 
-// EXPECT returns an object that allows the caller to indicate expected use
+// EXPECT returns an object that allows the caller to indicate expected use.
 func (m *MockBankKeeper) EXPECT() *MockBankKeeperMockRecorder {
 	return m.recorder
 }
 
-// GetAllBalances mocks base method
-func (m *MockBankKeeper) GetAllBalances(arg0 types0.Context, arg1 types0.AccAddress) types0.Coins {
+// GetAllBalances mocks base method.
+func (m *MockBankKeeper) GetAllBalances(ctx types0.Context, addr types0.AccAddress) types0.Coins {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetAllBalances", arg0, arg1)
+	ret := m.ctrl.Call(m, "GetAllBalances", ctx, addr)
 	ret0, _ := ret[0].(types0.Coins)
 	return ret0
 }
 
-// GetAllBalances indicates an expected call of GetAllBalances
-func (mr *MockBankKeeperMockRecorder) GetAllBalances(arg0, arg1 interface{}) *gomock.Call {
+// GetAllBalances indicates an expected call of GetAllBalances.
+func (mr *MockBankKeeperMockRecorder) GetAllBalances(ctx, addr interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllBalances", reflect.TypeOf((*MockBankKeeper)(nil).GetAllBalances), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllBalances", reflect.TypeOf((*MockBankKeeper)(nil).GetAllBalances), ctx, addr)
 }
 
-// GetBalance mocks base method
-func (m *MockBankKeeper) GetBalance(arg0 types0.Context, arg1 types0.AccAddress, arg2 string) types0.Coin {
+// GetBalance mocks base method.
+func (m *MockBankKeeper) GetBalance(ctx types0.Context, addr types0.AccAddress, denom string) types0.Coin {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetBalance", arg0, arg1, arg2)
+	ret := m.ctrl.Call(m, "GetBalance", ctx, addr, denom)
 	ret0, _ := ret[0].(types0.Coin)
 	return ret0
 }
 
-// GetBalance indicates an expected call of GetBalance
-func (mr *MockBankKeeperMockRecorder) GetBalance(arg0, arg1, arg2 interface{}) *gomock.Call {
+// GetBalance indicates an expected call of GetBalance.
+func (mr *MockBankKeeperMockRecorder) GetBalance(ctx, addr, denom interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBalance", reflect.TypeOf((*MockBankKeeper)(nil).GetBalance), arg0, arg1, arg2)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBalance", reflect.TypeOf((*MockBankKeeper)(nil).GetBalance), ctx, addr, denom)
 }
 
-// SendCoinsFromAccountToModule mocks base method
-func (m *MockBankKeeper) SendCoinsFromAccountToModule(arg0 types0.Context, arg1 types0.AccAddress, arg2 string, arg3 types0.Coins) error {
+// SendCoinsFromAccountToModule mocks base method.
+func (m *MockBankKeeper) SendCoinsFromAccountToModule(ctx types0.Context, senderAddr types0.AccAddress, recipientModule string, amt types0.Coins) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "SendCoinsFromAccountToModule", arg0, arg1, arg2, arg3)
+	ret := m.ctrl.Call(m, "SendCoinsFromAccountToModule", ctx, senderAddr, recipientModule, amt)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
-// SendCoinsFromAccountToModule indicates an expected call of SendCoinsFromAccountToModule
-func (mr *MockBankKeeperMockRecorder) SendCoinsFromAccountToModule(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
+// SendCoinsFromAccountToModule indicates an expected call of SendCoinsFromAccountToModule.
+func (mr *MockBankKeeperMockRecorder) SendCoinsFromAccountToModule(ctx, senderAddr, recipientModule, amt interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromAccountToModule", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromAccountToModule), arg0, arg1, arg2, arg3)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromAccountToModule", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromAccountToModule), ctx, senderAddr, recipientModule, amt)
 }
 
-// SendCoinsFromModuleToAccount mocks base method
-func (m *MockBankKeeper) SendCoinsFromModuleToAccount(arg0 types0.Context, arg1 string, arg2 types0.AccAddress, arg3 types0.Coins) error {
+// SendCoinsFromModuleToAccount mocks base method.
+func (m *MockBankKeeper) SendCoinsFromModuleToAccount(ctx types0.Context, senderModule string, recipientAddr types0.AccAddress, amt types0.Coins) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "SendCoinsFromModuleToAccount", arg0, arg1, arg2, arg3)
+	ret := m.ctrl.Call(m, "SendCoinsFromModuleToAccount", ctx, senderModule, recipientAddr, amt)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
-// SendCoinsFromModuleToAccount indicates an expected call of SendCoinsFromModuleToAccount
-func (mr *MockBankKeeperMockRecorder) SendCoinsFromModuleToAccount(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
+// SendCoinsFromModuleToAccount indicates an expected call of SendCoinsFromModuleToAccount.
+func (mr *MockBankKeeperMockRecorder) SendCoinsFromModuleToAccount(ctx, senderModule, recipientAddr, amt interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromModuleToAccount", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromModuleToAccount), arg0, arg1, arg2, arg3)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromModuleToAccount", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromModuleToAccount), ctx, senderModule, recipientAddr, amt)
 }
 
-// SpendableCoins mocks base method
-func (m *MockBankKeeper) SpendableCoins(arg0 types0.Context, arg1 types0.AccAddress) types0.Coins {
+// SpendableCoins mocks base method.
+func (m *MockBankKeeper) SpendableCoins(ctx types0.Context, addr types0.AccAddress) types0.Coins {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "SpendableCoins", arg0, arg1)
+	ret := m.ctrl.Call(m, "SpendableCoins", ctx, addr)
 	ret0, _ := ret[0].(types0.Coins)
 	return ret0
 }
 
-// SpendableCoins indicates an expected call of SpendableCoins
-func (mr *MockBankKeeperMockRecorder) SpendableCoins(arg0, arg1 interface{}) *gomock.Call {
+// SpendableCoins indicates an expected call of SpendableCoins.
+func (mr *MockBankKeeperMockRecorder) SpendableCoins(ctx, addr interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpendableCoins", reflect.TypeOf((*MockBankKeeper)(nil).SpendableCoins), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpendableCoins", reflect.TypeOf((*MockBankKeeper)(nil).SpendableCoins), ctx, addr)
 }
 
-// MockSpKeeper is a mock of SpKeeper interface
+// MockSpKeeper is a mock of SpKeeper interface.
 type MockSpKeeper struct {
 	ctrl     *gomock.Controller
 	recorder *MockSpKeeperMockRecorder
 }
 
-// MockSpKeeperMockRecorder is the mock recorder for MockSpKeeper
+// MockSpKeeperMockRecorder is the mock recorder for MockSpKeeper.
 type MockSpKeeperMockRecorder struct {
 	mock *MockSpKeeper
 }
 
-// NewMockSpKeeper creates a new mock instance
+// NewMockSpKeeper creates a new mock instance.
 func NewMockSpKeeper(ctrl *gomock.Controller) *MockSpKeeper {
 	mock := &MockSpKeeper{ctrl: ctrl}
 	mock.recorder = &MockSpKeeperMockRecorder{mock}
 	return mock
 }
 
-// EXPECT returns an object that allows the caller to indicate expected use
+// EXPECT returns an object that allows the caller to indicate expected use.
 func (m *MockSpKeeper) EXPECT() *MockSpKeeperMockRecorder {
 	return m.recorder
 }
 
-// GetSecondarySpStorePriceByTime mocks base method
-func (m *MockSpKeeper) GetSecondarySpStorePriceByTime(arg0 types0.Context, arg1 int64) (types.SecondarySpStorePrice, error) {
+// GetSecondarySpStorePriceByTime mocks base method.
+func (m *MockSpKeeper) GetSecondarySpStorePriceByTime(ctx types0.Context, time int64) (types.SecondarySpStorePrice, error) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetSecondarySpStorePriceByTime", arg0, arg1)
+	ret := m.ctrl.Call(m, "GetSecondarySpStorePriceByTime", ctx, time)
 	ret0, _ := ret[0].(types.SecondarySpStorePrice)
 	ret1, _ := ret[1].(error)
 	return ret0, ret1
 }
 
-// GetSecondarySpStorePriceByTime indicates an expected call of GetSecondarySpStorePriceByTime
-func (mr *MockSpKeeperMockRecorder) GetSecondarySpStorePriceByTime(arg0, arg1 interface{}) *gomock.Call {
+// GetSecondarySpStorePriceByTime indicates an expected call of GetSecondarySpStorePriceByTime.
+func (mr *MockSpKeeperMockRecorder) GetSecondarySpStorePriceByTime(ctx, time interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSecondarySpStorePriceByTime", reflect.TypeOf((*MockSpKeeper)(nil).GetSecondarySpStorePriceByTime), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSecondarySpStorePriceByTime", reflect.TypeOf((*MockSpKeeper)(nil).GetSecondarySpStorePriceByTime), ctx, time)
 }
 
-// GetSpStoragePriceByTime mocks base method
-func (m *MockSpKeeper) GetSpStoragePriceByTime(arg0 types0.Context, arg1 types0.AccAddress, arg2 int64) (types.SpStoragePrice, error) {
+// GetSpStoragePriceByTime mocks base method.
+func (m *MockSpKeeper) GetSpStoragePriceByTime(ctx types0.Context, spId uint32, time int64) (types.SpStoragePrice, error) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetSpStoragePriceByTime", arg0, arg1, arg2)
+	ret := m.ctrl.Call(m, "GetSpStoragePriceByTime", ctx, spId, time)
 	ret0, _ := ret[0].(types.SpStoragePrice)
 	ret1, _ := ret[1].(error)
 	return ret0, ret1
 }
 
-// GetSpStoragePriceByTime indicates an expected call of GetSpStoragePriceByTime
-func (mr *MockSpKeeperMockRecorder) GetSpStoragePriceByTime(arg0, arg1, arg2 interface{}) *gomock.Call {
+// GetSpStoragePriceByTime indicates an expected call of GetSpStoragePriceByTime.
+func (mr *MockSpKeeperMockRecorder) GetSpStoragePriceByTime(ctx, spId, time interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSpStoragePriceByTime", reflect.TypeOf((*MockSpKeeper)(nil).GetSpStoragePriceByTime), arg0, arg1, arg2)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSpStoragePriceByTime", reflect.TypeOf((*MockSpKeeper)(nil).GetSpStoragePriceByTime), ctx, spId, time)
 }
diff --git a/x/payment/types/keys.go b/x/payment/types/keys.go
index 26edf5225..776d07a2c 100644
--- a/x/payment/types/keys.go
+++ b/x/payment/types/keys.go
@@ -3,6 +3,7 @@ package types
 import (
 	"encoding/binary"
 
+	sdkmath "cosmossdk.io/math"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 )
 
@@ -22,11 +23,13 @@ const (
 
 var (
 	AutoSettleRecordKeyPrefix    = []byte{0x01}
-	StreamRecordKeyPrefix        = []byte{0x02}
-	PaymentAccountCountKeyPrefix = []byte{0x03}
-	PaymentAccountKeyPrefix      = []byte{0x04}
-	ParamsKey                    = []byte{0x05}
-	VersionedParamsKeyPrefix     = []byte{0x06}
+	AutoResumeRecordKeyPrefix    = []byte{0x02}
+	StreamRecordKeyPrefix        = []byte{0x03}
+	PaymentAccountCountKeyPrefix = []byte{0x04}
+	PaymentAccountKeyPrefix      = []byte{0x05}
+	OutFlowKeyPrefix             = []byte{0x06}
+	ParamsKey                    = []byte{0x07}
+	VersionedParamsKeyPrefix     = []byte{0x08}
 )
 
 // AutoSettleRecordKey returns the store key to retrieve a AutoSettleRecord from the index fields
@@ -52,6 +55,29 @@ func ParseAutoSettleRecordKey(key []byte) (res AutoSettleRecord) {
 	return
 }
 
+// AutoResumeRecordKey returns the store key to retrieve a AutoResumeRecord from the index fields
+func AutoResumeRecordKey(
+	timestamp int64,
+	addr sdk.AccAddress,
+) []byte {
+	var key []byte
+
+	timestampBytes := make([]byte, 8)
+	binary.BigEndian.PutUint64(timestampBytes, uint64(timestamp))
+	key = append(key, timestampBytes...)
+
+	addrBytes := []byte(addr)
+	key = append(key, addrBytes...)
+
+	return key
+}
+
+func ParseAutoResumeRecordKey(key []byte) (res AutoResumeRecord) {
+	res.Timestamp = int64(binary.BigEndian.Uint64(key[0:8]))
+	res.Addr = sdk.AccAddress(key[8:]).String()
+	return
+}
+
 // PaymentAccountKey returns the store key to retrieve a PaymentAccount from the index fields
 func PaymentAccountKey(
 	addr sdk.AccAddress,
@@ -73,6 +99,41 @@ func StreamRecordKey(
 	return account
 }
 
+func OutFlowKey(
+	addr sdk.AccAddress,
+	status OutFlowStatus,
+	toAddr sdk.AccAddress) []byte {
+	key := addr.Bytes()
+	if status == OUT_FLOW_STATUS_ACTIVE {
+		key = append(key, []byte{0x0}...)
+	} else {
+		key = append(key, []byte{0x1}...)
+	}
+	if toAddr != nil && !toAddr.Empty() {
+		key = append(key, toAddr.Bytes()...)
+	}
+	return key
+}
+
+func ParseOutFlowKey(key []byte) (addr sdk.AccAddress, res OutFlow) {
+	addr = key[0:20]
+	if key[20] == byte(0) {
+		res.Status = OUT_FLOW_STATUS_ACTIVE
+	} else {
+		res.Status = OUT_FLOW_STATUS_FROZEN
+	}
+	res.ToAddress = sdk.AccAddress(key[21:]).String()
+	return
+}
+
+func ParseOutFlowValue(value []byte) sdkmath.Int {
+	rate := sdk.ZeroInt()
+	if err := rate.Unmarshal(value); err != nil {
+		panic("should not happen")
+	}
+	return rate
+}
+
 // VersionedParamsKey return multi-version params store key
 func VersionedParamsKey(timestamp int64) []byte {
 	bz := make([]byte, 8)
diff --git a/x/payment/types/base.pb.go b/x/payment/types/out_flow.pb.go
similarity index 57%
rename from x/payment/types/base.pb.go
rename to x/payment/types/out_flow.pb.go
index 3b6762af7..293627e1c 100644
--- a/x/payment/types/base.pb.go
+++ b/x/payment/types/out_flow.pb.go
@@ -1,5 +1,5 @@
 // Code generated by protoc-gen-gogo. DO NOT EDIT.
-// source: greenfield/payment/base.proto
+// source: greenfield/payment/out_flow.proto
 
 package types
 
@@ -25,34 +25,32 @@ var _ = math.Inf
 // proto package needs to be updated.
 const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
 
-// StreamAccountStatus defines the status of a stream account
-type StreamAccountStatus int32
+// OutFlowStatus defines the status of a out flow
+type OutFlowStatus int32
 
 const (
-	// STREAM_ACCOUNT_STATUS_ACTIVE defines the active status of a stream account.
-	STREAM_ACCOUNT_STATUS_ACTIVE StreamAccountStatus = 0
-	// STREAM_ACCOUNT_STATUS_FROZEN defines the frozen status of a stream account.
-	// A frozen stream account cannot be used as payment address for buckets.
-	// It can be unfrozen by depositing more BNB to the stream account.
-	STREAM_ACCOUNT_STATUS_FROZEN StreamAccountStatus = 1
+	// OUT_FLOW_STATUS_ACTIVE defines the active status of a out flow.
+	OUT_FLOW_STATUS_ACTIVE OutFlowStatus = 0
+	// OUT_FLOW_STATUS_FROZEN defines the frozen status of a out flow.
+	OUT_FLOW_STATUS_FROZEN OutFlowStatus = 1
 )
 
-var StreamAccountStatus_name = map[int32]string{
-	0: "STREAM_ACCOUNT_STATUS_ACTIVE",
-	1: "STREAM_ACCOUNT_STATUS_FROZEN",
+var OutFlowStatus_name = map[int32]string{
+	0: "OUT_FLOW_STATUS_ACTIVE",
+	1: "OUT_FLOW_STATUS_FROZEN",
 }
 
-var StreamAccountStatus_value = map[string]int32{
-	"STREAM_ACCOUNT_STATUS_ACTIVE": 0,
-	"STREAM_ACCOUNT_STATUS_FROZEN": 1,
+var OutFlowStatus_value = map[string]int32{
+	"OUT_FLOW_STATUS_ACTIVE": 0,
+	"OUT_FLOW_STATUS_FROZEN": 1,
 }
 
-func (x StreamAccountStatus) String() string {
-	return proto.EnumName(StreamAccountStatus_name, int32(x))
+func (x OutFlowStatus) String() string {
+	return proto.EnumName(OutFlowStatus_name, int32(x))
 }
 
-func (StreamAccountStatus) EnumDescriptor() ([]byte, []int) {
-	return fileDescriptor_cff28fb00f42b060, []int{0}
+func (OutFlowStatus) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptor_5659e1072949c467, []int{0}
 }
 
 // OutFlow defines the accumulative outflow stream rate in BNB
@@ -62,13 +60,15 @@ type OutFlow struct {
 	ToAddress string `protobuf:"bytes,1,opt,name=to_address,json=toAddress,proto3" json:"to_address,omitempty"`
 	// flow rate
 	Rate github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,2,opt,name=rate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"rate"`
+	// status
+	Status OutFlowStatus `protobuf:"varint,3,opt,name=status,proto3,enum=greenfield.payment.OutFlowStatus" json:"status,omitempty"`
 }
 
 func (m *OutFlow) Reset()         { *m = OutFlow{} }
 func (m *OutFlow) String() string { return proto.CompactTextString(m) }
 func (*OutFlow) ProtoMessage()    {}
 func (*OutFlow) Descriptor() ([]byte, []int) {
-	return fileDescriptor_cff28fb00f42b060, []int{0}
+	return fileDescriptor_5659e1072949c467, []int{0}
 }
 func (m *OutFlow) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -104,36 +104,44 @@ func (m *OutFlow) GetToAddress() string {
 	return ""
 }
 
+func (m *OutFlow) GetStatus() OutFlowStatus {
+	if m != nil {
+		return m.Status
+	}
+	return OUT_FLOW_STATUS_ACTIVE
+}
+
 func init() {
-	proto.RegisterEnum("greenfield.payment.StreamAccountStatus", StreamAccountStatus_name, StreamAccountStatus_value)
+	proto.RegisterEnum("greenfield.payment.OutFlowStatus", OutFlowStatus_name, OutFlowStatus_value)
 	proto.RegisterType((*OutFlow)(nil), "greenfield.payment.OutFlow")
 }
 
-func init() { proto.RegisterFile("greenfield/payment/base.proto", fileDescriptor_cff28fb00f42b060) }
+func init() { proto.RegisterFile("greenfield/payment/out_flow.proto", fileDescriptor_5659e1072949c467) }
 
-var fileDescriptor_cff28fb00f42b060 = []byte{
-	// 335 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4d, 0x2f, 0x4a, 0x4d,
-	0xcd, 0x4b, 0xcb, 0x4c, 0xcd, 0x49, 0xd1, 0x2f, 0x48, 0xac, 0xcc, 0x4d, 0xcd, 0x2b, 0xd1, 0x4f,
-	0x4a, 0x2c, 0x4e, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x42, 0x48, 0xeb, 0x41, 0xa5,
-	0xa5, 0x24, 0x93, 0xf3, 0x8b, 0x73, 0xf3, 0x8b, 0xe3, 0xc1, 0x2a, 0xf4, 0x21, 0x1c, 0x88, 0x72,
-	0x29, 0x91, 0xf4, 0xfc, 0xf4, 0x7c, 0x88, 0x38, 0x88, 0x05, 0x11, 0x55, 0x9a, 0xc2, 0xc8, 0xc5,
-	0xee, 0x5f, 0x5a, 0xe2, 0x96, 0x93, 0x5f, 0x2e, 0x64, 0xce, 0xc5, 0x55, 0x92, 0x1f, 0x9f, 0x98,
-	0x92, 0x52, 0x94, 0x5a, 0x5c, 0x2c, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0xe9, 0x24, 0x71, 0x69, 0x8b,
-	0xae, 0x08, 0xd4, 0x1c, 0x47, 0x88, 0x4c, 0x70, 0x49, 0x51, 0x66, 0x5e, 0x7a, 0x10, 0x67, 0x49,
-	0x3e, 0x54, 0x40, 0x28, 0x80, 0x8b, 0xa5, 0x28, 0xb1, 0x24, 0x55, 0x82, 0x09, 0xac, 0xc5, 0xe6,
-	0xc4, 0x3d, 0x79, 0x86, 0x5b, 0xf7, 0xe4, 0xd5, 0xd2, 0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, 0x92,
-	0xf3, 0x73, 0xa1, 0x2e, 0x81, 0x52, 0xba, 0xc5, 0x29, 0xd9, 0xfa, 0x25, 0x95, 0x05, 0xa9, 0xc5,
-	0x7a, 0x9e, 0x79, 0x25, 0x97, 0xb6, 0xe8, 0x72, 0x41, 0x2d, 0xf0, 0xcc, 0x2b, 0x09, 0x02, 0x9b,
-	0xa4, 0x15, 0xcf, 0x25, 0x1c, 0x5c, 0x52, 0x94, 0x9a, 0x98, 0xeb, 0x98, 0x9c, 0x9c, 0x5f, 0x9a,
-	0x57, 0x12, 0x5c, 0x92, 0x58, 0x52, 0x5a, 0x2c, 0xa4, 0xc0, 0x25, 0x13, 0x1c, 0x12, 0xe4, 0xea,
-	0xe8, 0x1b, 0xef, 0xe8, 0xec, 0xec, 0x1f, 0xea, 0x17, 0x12, 0x1f, 0x1c, 0xe2, 0x18, 0x12, 0x1a,
-	0x1c, 0xef, 0xe8, 0x1c, 0xe2, 0x19, 0xe6, 0x2a, 0xc0, 0x80, 0x5b, 0x85, 0x5b, 0x90, 0x7f, 0x94,
-	0xab, 0x9f, 0x00, 0xa3, 0x14, 0x4b, 0xc7, 0x62, 0x39, 0x06, 0x27, 0xcf, 0x13, 0x8f, 0xe4, 0x18,
-	0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5,
-	0x18, 0x6e, 0x3c, 0x96, 0x63, 0x88, 0xd2, 0x47, 0x72, 0x76, 0x52, 0x5e, 0x92, 0x6e, 0x72, 0x46,
-	0x62, 0x66, 0x9e, 0x3e, 0x52, 0x54, 0x54, 0xc0, 0x23, 0x03, 0xec, 0x87, 0x24, 0x36, 0x70, 0x48,
-	0x1a, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x1c, 0xe3, 0x33, 0x19, 0xaf, 0x01, 0x00, 0x00,
+var fileDescriptor_5659e1072949c467 = []byte{
+	// 347 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4c, 0x2f, 0x4a, 0x4d,
+	0xcd, 0x4b, 0xcb, 0x4c, 0xcd, 0x49, 0xd1, 0x2f, 0x48, 0xac, 0xcc, 0x4d, 0xcd, 0x2b, 0xd1, 0xcf,
+	0x2f, 0x2d, 0x89, 0x4f, 0xcb, 0xc9, 0x2f, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x42,
+	0x28, 0xd1, 0x83, 0x2a, 0x91, 0x92, 0x4c, 0xce, 0x2f, 0xce, 0xcd, 0x2f, 0x8e, 0x07, 0xab, 0xd0,
+	0x87, 0x70, 0x20, 0xca, 0xa5, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0x21, 0xe2, 0x20, 0x16, 0x44, 0x54,
+	0xe9, 0x3c, 0x23, 0x17, 0xbb, 0x7f, 0x69, 0x89, 0x5b, 0x4e, 0x7e, 0xb9, 0x90, 0x39, 0x17, 0x57,
+	0x49, 0x7e, 0x7c, 0x62, 0x4a, 0x4a, 0x51, 0x6a, 0x71, 0xb1, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0xa7,
+	0x93, 0xc4, 0xa5, 0x2d, 0xba, 0x22, 0x50, 0x73, 0x1c, 0x21, 0x32, 0xc1, 0x25, 0x45, 0x99, 0x79,
+	0xe9, 0x41, 0x9c, 0x25, 0xf9, 0x50, 0x01, 0xa1, 0x00, 0x2e, 0x96, 0xa2, 0xc4, 0x92, 0x54, 0x09,
+	0x26, 0xb0, 0x16, 0x9b, 0x13, 0xf7, 0xe4, 0x19, 0x6e, 0xdd, 0x93, 0x57, 0x4b, 0xcf, 0x2c, 0xc9,
+	0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0x85, 0xba, 0x04, 0x4a, 0xe9, 0x16, 0xa7, 0x64, 0xeb, 0x97,
+	0x54, 0x16, 0xa4, 0x16, 0xeb, 0x79, 0xe6, 0x95, 0x5c, 0xda, 0xa2, 0xcb, 0x05, 0xb5, 0xc0, 0x33,
+	0xaf, 0x24, 0x08, 0x6c, 0x92, 0x90, 0x25, 0x17, 0x5b, 0x71, 0x49, 0x62, 0x49, 0x69, 0xb1, 0x04,
+	0xb3, 0x02, 0xa3, 0x06, 0x9f, 0x91, 0xa2, 0x1e, 0xa6, 0x67, 0xf5, 0xa0, 0xee, 0x0e, 0x06, 0x2b,
+	0x0c, 0x82, 0x6a, 0xd0, 0xf2, 0xe5, 0xe2, 0x45, 0x91, 0x10, 0x92, 0xe2, 0x12, 0xf3, 0x0f, 0x0d,
+	0x89, 0x77, 0xf3, 0xf1, 0x0f, 0x8f, 0x0f, 0x0e, 0x71, 0x0c, 0x09, 0x0d, 0x8e, 0x77, 0x74, 0x0e,
+	0xf1, 0x0c, 0x73, 0x15, 0x60, 0xc0, 0x26, 0xe7, 0x16, 0xe4, 0x1f, 0xe5, 0xea, 0x27, 0xc0, 0x28,
+	0xc5, 0xd2, 0xb1, 0x58, 0x8e, 0xc1, 0xc9, 0xf3, 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18,
+	0x1f, 0x3c, 0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, 0xe1, 0xc2, 0x63, 0x39, 0x86, 0x1b, 0x8f, 0xe5,
+	0x18, 0xa2, 0xf4, 0x91, 0xfc, 0x97, 0x94, 0x97, 0xa4, 0x9b, 0x9c, 0x91, 0x98, 0x99, 0xa7, 0x8f,
+	0x14, 0x6f, 0x15, 0xf0, 0x98, 0x03, 0x7b, 0x36, 0x89, 0x0d, 0x1c, 0xe4, 0xc6, 0x80, 0x00, 0x00,
+	0x00, 0xff, 0xff, 0x68, 0x18, 0x2e, 0x36, 0xdc, 0x01, 0x00, 0x00,
 }
 
 func (m *OutFlow) Marshal() (dAtA []byte, err error) {
@@ -156,28 +164,33 @@ func (m *OutFlow) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	_ = i
 	var l int
 	_ = l
+	if m.Status != 0 {
+		i = encodeVarintOutFlow(dAtA, i, uint64(m.Status))
+		i--
+		dAtA[i] = 0x18
+	}
 	{
 		size := m.Rate.Size()
 		i -= size
 		if _, err := m.Rate.MarshalTo(dAtA[i:]); err != nil {
 			return 0, err
 		}
-		i = encodeVarintBase(dAtA, i, uint64(size))
+		i = encodeVarintOutFlow(dAtA, i, uint64(size))
 	}
 	i--
 	dAtA[i] = 0x12
 	if len(m.ToAddress) > 0 {
 		i -= len(m.ToAddress)
 		copy(dAtA[i:], m.ToAddress)
-		i = encodeVarintBase(dAtA, i, uint64(len(m.ToAddress)))
+		i = encodeVarintOutFlow(dAtA, i, uint64(len(m.ToAddress)))
 		i--
 		dAtA[i] = 0xa
 	}
 	return len(dAtA) - i, nil
 }
 
-func encodeVarintBase(dAtA []byte, offset int, v uint64) int {
-	offset -= sovBase(v)
+func encodeVarintOutFlow(dAtA []byte, offset int, v uint64) int {
+	offset -= sovOutFlow(v)
 	base := offset
 	for v >= 1<<7 {
 		dAtA[offset] = uint8(v&0x7f | 0x80)
@@ -195,18 +208,21 @@ func (m *OutFlow) Size() (n int) {
 	_ = l
 	l = len(m.ToAddress)
 	if l > 0 {
-		n += 1 + l + sovBase(uint64(l))
+		n += 1 + l + sovOutFlow(uint64(l))
 	}
 	l = m.Rate.Size()
-	n += 1 + l + sovBase(uint64(l))
+	n += 1 + l + sovOutFlow(uint64(l))
+	if m.Status != 0 {
+		n += 1 + sovOutFlow(uint64(m.Status))
+	}
 	return n
 }
 
-func sovBase(x uint64) (n int) {
+func sovOutFlow(x uint64) (n int) {
 	return (math_bits.Len64(x|1) + 6) / 7
 }
-func sozBase(x uint64) (n int) {
-	return sovBase(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+func sozOutFlow(x uint64) (n int) {
+	return sovOutFlow(uint64((x << 1) ^ uint64((int64(x) >> 63))))
 }
 func (m *OutFlow) Unmarshal(dAtA []byte) error {
 	l := len(dAtA)
@@ -216,7 +232,7 @@ func (m *OutFlow) Unmarshal(dAtA []byte) error {
 		var wire uint64
 		for shift := uint(0); ; shift += 7 {
 			if shift >= 64 {
-				return ErrIntOverflowBase
+				return ErrIntOverflowOutFlow
 			}
 			if iNdEx >= l {
 				return io.ErrUnexpectedEOF
@@ -244,7 +260,7 @@ func (m *OutFlow) Unmarshal(dAtA []byte) error {
 			var stringLen uint64
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
-					return ErrIntOverflowBase
+					return ErrIntOverflowOutFlow
 				}
 				if iNdEx >= l {
 					return io.ErrUnexpectedEOF
@@ -258,11 +274,11 @@ func (m *OutFlow) Unmarshal(dAtA []byte) error {
 			}
 			intStringLen := int(stringLen)
 			if intStringLen < 0 {
-				return ErrInvalidLengthBase
+				return ErrInvalidLengthOutFlow
 			}
 			postIndex := iNdEx + intStringLen
 			if postIndex < 0 {
-				return ErrInvalidLengthBase
+				return ErrInvalidLengthOutFlow
 			}
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
@@ -276,7 +292,7 @@ func (m *OutFlow) Unmarshal(dAtA []byte) error {
 			var stringLen uint64
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
-					return ErrIntOverflowBase
+					return ErrIntOverflowOutFlow
 				}
 				if iNdEx >= l {
 					return io.ErrUnexpectedEOF
@@ -290,11 +306,11 @@ func (m *OutFlow) Unmarshal(dAtA []byte) error {
 			}
 			intStringLen := int(stringLen)
 			if intStringLen < 0 {
-				return ErrInvalidLengthBase
+				return ErrInvalidLengthOutFlow
 			}
 			postIndex := iNdEx + intStringLen
 			if postIndex < 0 {
-				return ErrInvalidLengthBase
+				return ErrInvalidLengthOutFlow
 			}
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
@@ -303,14 +319,33 @@ func (m *OutFlow) Unmarshal(dAtA []byte) error {
 				return err
 			}
 			iNdEx = postIndex
+		case 3:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType)
+			}
+			m.Status = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowOutFlow
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.Status |= OutFlowStatus(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
 		default:
 			iNdEx = preIndex
-			skippy, err := skipBase(dAtA[iNdEx:])
+			skippy, err := skipOutFlow(dAtA[iNdEx:])
 			if err != nil {
 				return err
 			}
 			if (skippy < 0) || (iNdEx+skippy) < 0 {
-				return ErrInvalidLengthBase
+				return ErrInvalidLengthOutFlow
 			}
 			if (iNdEx + skippy) > l {
 				return io.ErrUnexpectedEOF
@@ -324,7 +359,7 @@ func (m *OutFlow) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
-func skipBase(dAtA []byte) (n int, err error) {
+func skipOutFlow(dAtA []byte) (n int, err error) {
 	l := len(dAtA)
 	iNdEx := 0
 	depth := 0
@@ -332,7 +367,7 @@ func skipBase(dAtA []byte) (n int, err error) {
 		var wire uint64
 		for shift := uint(0); ; shift += 7 {
 			if shift >= 64 {
-				return 0, ErrIntOverflowBase
+				return 0, ErrIntOverflowOutFlow
 			}
 			if iNdEx >= l {
 				return 0, io.ErrUnexpectedEOF
@@ -349,7 +384,7 @@ func skipBase(dAtA []byte) (n int, err error) {
 		case 0:
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
-					return 0, ErrIntOverflowBase
+					return 0, ErrIntOverflowOutFlow
 				}
 				if iNdEx >= l {
 					return 0, io.ErrUnexpectedEOF
@@ -365,7 +400,7 @@ func skipBase(dAtA []byte) (n int, err error) {
 			var length int
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
-					return 0, ErrIntOverflowBase
+					return 0, ErrIntOverflowOutFlow
 				}
 				if iNdEx >= l {
 					return 0, io.ErrUnexpectedEOF
@@ -378,14 +413,14 @@ func skipBase(dAtA []byte) (n int, err error) {
 				}
 			}
 			if length < 0 {
-				return 0, ErrInvalidLengthBase
+				return 0, ErrInvalidLengthOutFlow
 			}
 			iNdEx += length
 		case 3:
 			depth++
 		case 4:
 			if depth == 0 {
-				return 0, ErrUnexpectedEndOfGroupBase
+				return 0, ErrUnexpectedEndOfGroupOutFlow
 			}
 			depth--
 		case 5:
@@ -394,7 +429,7 @@ func skipBase(dAtA []byte) (n int, err error) {
 			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
 		}
 		if iNdEx < 0 {
-			return 0, ErrInvalidLengthBase
+			return 0, ErrInvalidLengthOutFlow
 		}
 		if depth == 0 {
 			return iNdEx, nil
@@ -404,7 +439,7 @@ func skipBase(dAtA []byte) (n int, err error) {
 }
 
 var (
-	ErrInvalidLengthBase        = fmt.Errorf("proto: negative length found during unmarshaling")
-	ErrIntOverflowBase          = fmt.Errorf("proto: integer overflow")
-	ErrUnexpectedEndOfGroupBase = fmt.Errorf("proto: unexpected end of group")
+	ErrInvalidLengthOutFlow        = fmt.Errorf("proto: negative length found during unmarshaling")
+	ErrIntOverflowOutFlow          = fmt.Errorf("proto: integer overflow")
+	ErrUnexpectedEndOfGroupOutFlow = fmt.Errorf("proto: unexpected end of group")
 )
diff --git a/x/payment/types/params.go b/x/payment/types/params.go
index 4a67fa1c6..13c9532f8 100644
--- a/x/payment/types/params.go
+++ b/x/payment/types/params.go
@@ -13,7 +13,8 @@ var (
 	KeyReserveTime              = []byte("ReserveTime")
 	KeyForcedSettleTime         = []byte("ForcedSettleTime")
 	KeyPaymentAccountCountLimit = []byte("PaymentAccountCountLimit")
-	KeyMaxAutoForceSettleNum    = []byte("MaxAutoForceSettleNum")
+	KeyMaxAutoSettleFlowCount   = []byte("MaxAutoSettleFlowCount")
+	KeyMaxAutoResumeFlowCount   = []byte("MaxAutoResumeFlowCount")
 	KeyFeeDenom                 = []byte("FeeDenom")
 	KeyValidatorTaxRate         = []byte("ValidatorTaxRate")
 
@@ -22,7 +23,8 @@ var (
 
 	DefaultForcedSettleTime         uint64 = 24 * 60 * 60 // 1 day
 	DefaultPaymentAccountCountLimit uint64 = 200
-	DefaultMaxAutoForceSettleNum    uint64 = 100
+	DefaultMaxAutoSettleFlowCount   uint64 = 100
+	DefaultMaxAutoResumeFlowCount   uint64 = 100
 	DefaultFeeDenom                 string = "BNB"
 )
 
@@ -37,14 +39,16 @@ func NewParams(
 	validatorTaxRate sdk.Dec,
 	forcedSettleTime uint64,
 	paymentAccountCountLimit uint64,
-	maxAutoForceSettleNum uint64,
+	MaxAutoSettleFlowCount uint64,
+	maxAutoResumeFlowCount uint64,
 	feeDenom string,
 ) Params {
 	return Params{
 		VersionedParams:          VersionedParams{ReserveTime: reserveTime, ValidatorTaxRate: validatorTaxRate},
 		ForcedSettleTime:         forcedSettleTime,
 		PaymentAccountCountLimit: paymentAccountCountLimit,
-		MaxAutoForceSettleNum:    maxAutoForceSettleNum,
+		MaxAutoSettleFlowCount:   MaxAutoSettleFlowCount,
+		MaxAutoResumeFlowCount:   maxAutoResumeFlowCount,
 		FeeDenom:                 feeDenom,
 	}
 }
@@ -56,7 +60,8 @@ func DefaultParams() Params {
 		DefaultValidatorTaxRate,
 		DefaultForcedSettleTime,
 		DefaultPaymentAccountCountLimit,
-		DefaultMaxAutoForceSettleNum,
+		DefaultMaxAutoSettleFlowCount,
+		DefaultMaxAutoResumeFlowCount,
 		DefaultFeeDenom,
 	)
 }
@@ -68,7 +73,8 @@ func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs {
 		paramtypes.NewParamSetPair(KeyValidatorTaxRate, &p.VersionedParams.ValidatorTaxRate, validateValidatorTaxRate),
 		paramtypes.NewParamSetPair(KeyForcedSettleTime, &p.ForcedSettleTime, validateForcedSettleTime),
 		paramtypes.NewParamSetPair(KeyPaymentAccountCountLimit, &p.PaymentAccountCountLimit, validatePaymentAccountCountLimit),
-		paramtypes.NewParamSetPair(KeyMaxAutoForceSettleNum, &p.MaxAutoForceSettleNum, validateMaxAutoForceSettleNum),
+		paramtypes.NewParamSetPair(KeyMaxAutoSettleFlowCount, &p.MaxAutoSettleFlowCount, validateMaxAutoSettleFlowCount),
+		paramtypes.NewParamSetPair(KeyMaxAutoResumeFlowCount, &p.MaxAutoResumeFlowCount, validateMaxAutoResumeFlowCount),
 		paramtypes.NewParamSetPair(KeyFeeDenom, &p.FeeDenom, validateFeeDenom),
 	}
 }
@@ -91,7 +97,11 @@ func (p Params) Validate() error {
 		return err
 	}
 
-	if err := validatePaymentAccountCountLimit(p.MaxAutoForceSettleNum); err != nil {
+	if err := validateMaxAutoSettleFlowCount(p.MaxAutoSettleFlowCount); err != nil {
+		return err
+	}
+
+	if err := validateMaxAutoResumeFlowCount(p.MaxAutoResumeFlowCount); err != nil {
 		return err
 	}
 
@@ -143,15 +153,29 @@ func validatePaymentAccountCountLimit(v interface{}) error {
 	return nil
 }
 
-// validateMaxAutoForceSettleNum validates the MaxAutoForceSettleNum param
-func validateMaxAutoForceSettleNum(v interface{}) error {
-	maxAutoForceSettleNum, ok := v.(uint64)
+// validateMaxAutoSettleFlowCount validates the MaxAutoSettleFlowCount param
+func validateMaxAutoSettleFlowCount(v interface{}) error {
+	maxAutoSettleFlowCount, ok := v.(uint64)
+	if !ok {
+		return fmt.Errorf("invalid parameter type: %T", v)
+	}
+
+	if maxAutoSettleFlowCount <= 0 {
+		return fmt.Errorf("max force settle flow count must be positive")
+	}
+
+	return nil
+}
+
+// validateMaxAutoResumeFlowCount validates the MaxAutoResumeFlowCount param
+func validateMaxAutoResumeFlowCount(v interface{}) error {
+	maxAutoResumeFlowCount, ok := v.(uint64)
 	if !ok {
 		return fmt.Errorf("invalid parameter type: %T", v)
 	}
 
-	if maxAutoForceSettleNum <= 0 {
-		return fmt.Errorf("max auto force settle num must be positive")
+	if maxAutoResumeFlowCount <= 0 {
+		return fmt.Errorf("max auto resume flow count must be positive")
 	}
 
 	return nil
diff --git a/x/payment/types/params.pb.go b/x/payment/types/params.pb.go
index cd37e93ad..b03031d4a 100644
--- a/x/payment/types/params.pb.go
+++ b/x/payment/types/params.pb.go
@@ -33,10 +33,12 @@ type Params struct {
 	// Time duration threshold of forced settlement.
 	// If dynamic balance is less than NetOutFlowRate * forcedSettleTime, the account can be forced settled.
 	ForcedSettleTime uint64 `protobuf:"varint,3,opt,name=forced_settle_time,json=forcedSettleTime,proto3" json:"forced_settle_time,omitempty" yaml:"forced_settle_time"`
-	// the maximum number of accounts that will be forced settled in one block
-	MaxAutoForceSettleNum uint64 `protobuf:"varint,4,opt,name=max_auto_force_settle_num,json=maxAutoForceSettleNum,proto3" json:"max_auto_force_settle_num,omitempty" yaml:"max_auto_force_settle_num"`
+	// the maximum number of flows that will be auto forced settled in one block
+	MaxAutoSettleFlowCount uint64 `protobuf:"varint,4,opt,name=max_auto_settle_flow_count,json=maxAutoSettleFlowCount,proto3" json:"max_auto_settle_flow_count,omitempty" yaml:"max_auto_settle_flow_count"`
+	// the maximum number of flows that will be auto resumed in one block
+	MaxAutoResumeFlowCount uint64 `protobuf:"varint,5,opt,name=max_auto_resume_flow_count,json=maxAutoResumeFlowCount,proto3" json:"max_auto_resume_flow_count,omitempty" yaml:"max_auto_resume_flow_count"`
 	// The denom of fee charged in payment module
-	FeeDenom string `protobuf:"bytes,5,opt,name=fee_denom,json=feeDenom,proto3" json:"fee_denom,omitempty" yaml:"fee_denom"`
+	FeeDenom string `protobuf:"bytes,6,opt,name=fee_denom,json=feeDenom,proto3" json:"fee_denom,omitempty" yaml:"fee_denom"`
 }
 
 func (m *Params) Reset()         { *m = Params{} }
@@ -93,9 +95,16 @@ func (m *Params) GetForcedSettleTime() uint64 {
 	return 0
 }
 
-func (m *Params) GetMaxAutoForceSettleNum() uint64 {
+func (m *Params) GetMaxAutoSettleFlowCount() uint64 {
 	if m != nil {
-		return m.MaxAutoForceSettleNum
+		return m.MaxAutoSettleFlowCount
+	}
+	return 0
+}
+
+func (m *Params) GetMaxAutoResumeFlowCount() uint64 {
+	if m != nil {
+		return m.MaxAutoResumeFlowCount
 	}
 	return 0
 }
@@ -163,38 +172,39 @@ func init() {
 func init() { proto.RegisterFile("greenfield/payment/params.proto", fileDescriptor_bd7d37632356c8f4) }
 
 var fileDescriptor_bd7d37632356c8f4 = []byte{
-	// 488 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x52, 0xc1, 0x6e, 0xd3, 0x30,
-	0x18, 0x6e, 0x58, 0x99, 0xa8, 0x87, 0xb4, 0xca, 0x0c, 0x91, 0x0e, 0x91, 0x54, 0x01, 0x4d, 0xbd,
-	0x34, 0x11, 0x70, 0x9b, 0xb8, 0x2c, 0x54, 0x48, 0x08, 0x84, 0x50, 0xa8, 0x38, 0x70, 0xc0, 0x72,
-	0x93, 0xbf, 0x5d, 0x20, 0x8e, 0xab, 0xc4, 0xa9, 0xd2, 0xb7, 0xe0, 0x25, 0x78, 0x03, 0x2e, 0xbc,
-	0xc1, 0x8e, 0x13, 0x27, 0xc4, 0x21, 0x42, 0xed, 0x1b, 0xe4, 0x09, 0x50, 0x6c, 0x77, 0x14, 0x2a,
-	0xb8, 0x38, 0xf6, 0xf7, 0x7d, 0xfe, 0x3e, 0x3b, 0xfe, 0x90, 0x3d, 0xcb, 0x00, 0xd2, 0x69, 0x0c,
-	0x49, 0xe4, 0xcd, 0xe9, 0x92, 0x41, 0x2a, 0xbc, 0x39, 0xcd, 0x28, 0xcb, 0xdd, 0x79, 0xc6, 0x05,
-	0xc7, 0xf8, 0xb7, 0xc0, 0xd5, 0x82, 0xe3, 0x5e, 0xc8, 0x73, 0xc6, 0x73, 0x22, 0x15, 0x9e, 0x5a,
-	0x28, 0xf9, 0xf1, 0xd1, 0x8c, 0xcf, 0xb8, 0xc2, 0x9b, 0x99, 0x42, 0x9d, 0xcf, 0x7b, 0x68, 0xff,
-	0xb5, 0x74, 0xc5, 0x63, 0xd4, 0x5d, 0x40, 0x96, 0xc7, 0x3c, 0x85, 0x88, 0xa8, 0x24, 0xd3, 0xe8,
-	0x1b, 0x83, 0x83, 0x47, 0xf7, 0xdd, 0xdd, 0x28, 0xf7, 0xed, 0x46, 0xab, 0xb6, 0xfb, 0xed, 0x8b,
-	0xca, 0x6e, 0x05, 0x87, 0x8b, 0x3f, 0x61, 0x0c, 0xe8, 0xae, 0xde, 0x41, 0x68, 0x18, 0xf2, 0x22,
-	0x15, 0x44, 0x8d, 0x49, 0xcc, 0x62, 0x61, 0x5e, 0xeb, 0x1b, 0x83, 0xb6, 0x7f, 0x52, 0x57, 0xb6,
-	0xb3, 0xa4, 0x2c, 0x39, 0x75, 0xfe, 0x23, 0x76, 0x02, 0x53, 0xb3, 0x67, 0x8a, 0x7c, 0xda, 0x0c,
-	0x2f, 0x1b, 0x0a, 0xbf, 0x40, 0x78, 0xca, 0xb3, 0x10, 0x22, 0x92, 0x83, 0x10, 0x09, 0x10, 0x11,
-	0x33, 0x30, 0xf7, 0xa4, 0xfb, 0xbd, 0xba, 0xb2, 0x7b, 0xca, 0x7d, 0x57, 0xe3, 0x04, 0x5d, 0x05,
-	0xbe, 0x91, 0xd8, 0x38, 0x66, 0x80, 0xdf, 0xa3, 0x1e, 0xa3, 0x25, 0xa1, 0x85, 0xe0, 0x44, 0x92,
-	0x9b, 0x0d, 0x69, 0xc1, 0xcc, 0xb6, 0xf4, 0x7c, 0x50, 0x57, 0x76, 0x5f, 0x79, 0xfe, 0x53, 0xea,
-	0x04, 0xb7, 0x19, 0x2d, 0xcf, 0x0a, 0xc1, 0x9f, 0x35, 0x8c, 0x0a, 0x78, 0x55, 0x30, 0xfc, 0x10,
-	0x75, 0xa6, 0x00, 0x24, 0x82, 0x94, 0x33, 0xf3, 0x7a, 0xdf, 0x18, 0x74, 0xfc, 0xa3, 0xba, 0xb2,
-	0xbb, 0xfa, 0x8c, 0x1b, 0xca, 0x09, 0x6e, 0x4c, 0x01, 0x46, 0x72, 0xfa, 0xd5, 0x40, 0x87, 0x7f,
-	0xfd, 0x71, 0x7c, 0x8a, 0x6e, 0x66, 0x90, 0x43, 0xb6, 0xd0, 0xb7, 0x35, 0xe4, 0xc9, 0xee, 0xd4,
-	0x95, 0x7d, 0x4b, 0x39, 0x6d, 0xb3, 0x4e, 0x70, 0xa0, 0x97, 0xf2, 0x8a, 0x1f, 0x10, 0x5e, 0xd0,
-	0x24, 0x8e, 0xa8, 0xe0, 0x19, 0x11, 0xb4, 0x24, 0x19, 0x15, 0x20, 0x5f, 0xa3, 0xe3, 0x3f, 0x69,
-	0x5e, 0xf2, 0x47, 0x65, 0x9f, 0xcc, 0x62, 0x71, 0x5e, 0x4c, 0xdc, 0x90, 0x33, 0x5d, 0x25, 0xfd,
-	0x19, 0xe6, 0xd1, 0x47, 0x4f, 0x2c, 0xe7, 0x90, 0xbb, 0x23, 0x08, 0xbf, 0x7d, 0x19, 0x22, 0xdd,
-	0xb4, 0x11, 0x84, 0x41, 0xf7, 0xca, 0x77, 0x4c, 0xcb, 0x80, 0x0a, 0xf0, 0x9f, 0x5f, 0xac, 0x2c,
-	0xe3, 0x72, 0x65, 0x19, 0x3f, 0x57, 0x96, 0xf1, 0x69, 0x6d, 0xb5, 0x2e, 0xd7, 0x56, 0xeb, 0xfb,
-	0xda, 0x6a, 0xbd, 0xf3, 0xb6, 0x12, 0x26, 0xe9, 0x64, 0x18, 0x9e, 0xd3, 0x38, 0xf5, 0xb6, 0x8a,
-	0x5f, 0x5e, 0x55, 0x5f, 0xc6, 0x4d, 0xf6, 0x65, 0x6b, 0x1f, 0xff, 0x0a, 0x00, 0x00, 0xff, 0xff,
-	0x0c, 0xc8, 0xf7, 0x19, 0x1d, 0x03, 0x00, 0x00,
+	// 509 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x53, 0xcd, 0x6e, 0xd3, 0x4c,
+	0x14, 0x8d, 0xbf, 0x2f, 0x44, 0x64, 0x8a, 0xd4, 0xc8, 0x54, 0xe0, 0x06, 0x61, 0x07, 0x23, 0xaa,
+	0x6c, 0x62, 0x0b, 0xd8, 0x55, 0x6c, 0x6a, 0x22, 0x24, 0x04, 0x0b, 0x64, 0x22, 0x16, 0x6c, 0x46,
+	0x13, 0xfb, 0x3a, 0x35, 0x78, 0x3c, 0xd1, 0x78, 0x9c, 0x3a, 0xcf, 0xc0, 0x86, 0x87, 0x61, 0xc3,
+	0x1b, 0x74, 0x59, 0xb1, 0x42, 0x2c, 0x2c, 0x94, 0xbc, 0x81, 0x9f, 0x00, 0x65, 0xc6, 0x69, 0x12,
+	0xa2, 0xb2, 0x99, 0x9f, 0x7b, 0xce, 0x9c, 0x73, 0x75, 0xe7, 0x5e, 0x64, 0x4d, 0x38, 0x40, 0x1a,
+	0xc5, 0x90, 0x84, 0xee, 0x94, 0xcc, 0x29, 0xa4, 0xc2, 0x9d, 0x12, 0x4e, 0x68, 0xe6, 0x4c, 0x39,
+	0x13, 0x4c, 0xd7, 0x37, 0x04, 0xa7, 0x26, 0x74, 0x8f, 0x03, 0x96, 0x51, 0x96, 0x61, 0xc9, 0x70,
+	0xd5, 0x45, 0xd1, 0xbb, 0x47, 0x13, 0x36, 0x61, 0x2a, 0xbe, 0x3a, 0xa9, 0xa8, 0xfd, 0xa5, 0x89,
+	0x5a, 0xef, 0xa4, 0xaa, 0x3e, 0x42, 0x9d, 0x19, 0xf0, 0x2c, 0x66, 0x29, 0x84, 0x58, 0x39, 0x19,
+	0x5a, 0x4f, 0xeb, 0x1f, 0x3c, 0x7b, 0xec, 0xec, 0x5b, 0x39, 0x1f, 0xd6, 0x5c, 0xf5, 0xdc, 0x6b,
+	0x5e, 0x96, 0x56, 0xc3, 0x3f, 0x9c, 0xed, 0x86, 0x75, 0x40, 0x0f, 0xea, 0x17, 0x98, 0x04, 0x01,
+	0xcb, 0x53, 0x81, 0xd5, 0x9a, 0xc4, 0x34, 0x16, 0xc6, 0x7f, 0x3d, 0xad, 0xdf, 0xf4, 0x4e, 0xaa,
+	0xd2, 0xb2, 0xe7, 0x84, 0x26, 0xa7, 0xf6, 0x3f, 0xc8, 0xb6, 0x6f, 0xd4, 0xe8, 0x99, 0x02, 0x5f,
+	0xae, 0x96, 0xb7, 0x2b, 0x48, 0x7f, 0x83, 0xf4, 0x88, 0xf1, 0x00, 0x42, 0x9c, 0x81, 0x10, 0x09,
+	0x60, 0x11, 0x53, 0x30, 0xfe, 0x97, 0xea, 0x0f, 0xab, 0xd2, 0x3a, 0x56, 0xea, 0xfb, 0x1c, 0xdb,
+	0xef, 0xa8, 0xe0, 0x7b, 0x19, 0x1b, 0xc5, 0x14, 0x74, 0x82, 0xba, 0x94, 0x14, 0x98, 0xe4, 0x82,
+	0xad, 0xa9, 0x51, 0xc2, 0x2e, 0x54, 0x2e, 0x46, 0x53, 0x8a, 0x3e, 0xa9, 0x4a, 0xeb, 0x91, 0x12,
+	0xbd, 0x99, 0x6b, 0xfb, 0xf7, 0x28, 0x29, 0xce, 0x72, 0xc1, 0x94, 0xfa, 0xab, 0x84, 0x5d, 0xc8,
+	0xa4, 0x77, 0x2c, 0x38, 0x64, 0x39, 0xdd, 0xb1, 0xb8, 0x75, 0xa3, 0xc5, 0x1e, 0x77, 0x63, 0xe1,
+	0x4b, 0x68, 0x63, 0xf1, 0x14, 0xb5, 0x23, 0x00, 0x1c, 0x42, 0xca, 0xa8, 0xd1, 0xea, 0x69, 0xfd,
+	0xb6, 0x77, 0x54, 0x95, 0x56, 0xa7, 0xae, 0xc4, 0x1a, 0xb2, 0xfd, 0xdb, 0x11, 0xc0, 0x50, 0x1e,
+	0xbf, 0x6b, 0xe8, 0xf0, 0xaf, 0x7f, 0xd5, 0x4f, 0xd1, 0x1d, 0x0e, 0x19, 0xf0, 0x59, 0x5d, 0x53,
+	0x4d, 0xe6, 0x76, 0xbf, 0x2a, 0xad, 0xbb, 0x4a, 0x69, 0x1b, 0xb5, 0xfd, 0x83, 0xfa, 0x2a, 0x0b,
+	0xf9, 0x09, 0xe9, 0x33, 0x92, 0xc4, 0x21, 0x11, 0x8c, 0x63, 0x41, 0x0a, 0xcc, 0x89, 0x00, 0xf9,
+	0xe7, 0x6d, 0xef, 0xc5, 0xaa, 0x5f, 0x7e, 0x95, 0xd6, 0xc9, 0x24, 0x16, 0xe7, 0xf9, 0xd8, 0x09,
+	0x18, 0xad, 0x1b, 0xb6, 0xde, 0x06, 0x59, 0xf8, 0xd9, 0x15, 0xf3, 0x29, 0x64, 0xce, 0x10, 0x82,
+	0x1f, 0xdf, 0x06, 0xa8, 0xee, 0xe7, 0x21, 0x04, 0x7e, 0xe7, 0x5a, 0x77, 0x44, 0x0a, 0x9f, 0x08,
+	0xf0, 0x5e, 0x5f, 0x2e, 0x4c, 0xed, 0x6a, 0x61, 0x6a, 0xbf, 0x17, 0xa6, 0xf6, 0x75, 0x69, 0x36,
+	0xae, 0x96, 0x66, 0xe3, 0xe7, 0xd2, 0x6c, 0x7c, 0x74, 0xb7, 0x1c, 0xc6, 0xe9, 0x78, 0x10, 0x9c,
+	0x93, 0x38, 0x75, 0xb7, 0xc6, 0xab, 0xb8, 0x1e, 0x30, 0x69, 0x37, 0x6e, 0xc9, 0xd9, 0x78, 0xfe,
+	0x27, 0x00, 0x00, 0xff, 0xff, 0xad, 0xa0, 0x65, 0x82, 0x83, 0x03, 0x00, 0x00,
 }
 
 func (m *Params) Marshal() (dAtA []byte, err error) {
@@ -222,10 +232,15 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		copy(dAtA[i:], m.FeeDenom)
 		i = encodeVarintParams(dAtA, i, uint64(len(m.FeeDenom)))
 		i--
-		dAtA[i] = 0x2a
+		dAtA[i] = 0x32
+	}
+	if m.MaxAutoResumeFlowCount != 0 {
+		i = encodeVarintParams(dAtA, i, uint64(m.MaxAutoResumeFlowCount))
+		i--
+		dAtA[i] = 0x28
 	}
-	if m.MaxAutoForceSettleNum != 0 {
-		i = encodeVarintParams(dAtA, i, uint64(m.MaxAutoForceSettleNum))
+	if m.MaxAutoSettleFlowCount != 0 {
+		i = encodeVarintParams(dAtA, i, uint64(m.MaxAutoSettleFlowCount))
 		i--
 		dAtA[i] = 0x20
 	}
@@ -315,8 +330,11 @@ func (m *Params) Size() (n int) {
 	if m.ForcedSettleTime != 0 {
 		n += 1 + sovParams(uint64(m.ForcedSettleTime))
 	}
-	if m.MaxAutoForceSettleNum != 0 {
-		n += 1 + sovParams(uint64(m.MaxAutoForceSettleNum))
+	if m.MaxAutoSettleFlowCount != 0 {
+		n += 1 + sovParams(uint64(m.MaxAutoSettleFlowCount))
+	}
+	if m.MaxAutoResumeFlowCount != 0 {
+		n += 1 + sovParams(uint64(m.MaxAutoResumeFlowCount))
 	}
 	l = len(m.FeeDenom)
 	if l > 0 {
@@ -447,9 +465,9 @@ func (m *Params) Unmarshal(dAtA []byte) error {
 			}
 		case 4:
 			if wireType != 0 {
-				return fmt.Errorf("proto: wrong wireType = %d for field MaxAutoForceSettleNum", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field MaxAutoSettleFlowCount", wireType)
 			}
-			m.MaxAutoForceSettleNum = 0
+			m.MaxAutoSettleFlowCount = 0
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowParams
@@ -459,12 +477,31 @@ func (m *Params) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				m.MaxAutoForceSettleNum |= uint64(b&0x7F) << shift
+				m.MaxAutoSettleFlowCount |= uint64(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
 		case 5:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field MaxAutoResumeFlowCount", wireType)
+			}
+			m.MaxAutoResumeFlowCount = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowParams
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.MaxAutoResumeFlowCount |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 6:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field FeeDenom", wireType)
 			}
diff --git a/x/payment/types/price.go b/x/payment/types/price.go
index 36ad05038..a1b144ae8 100644
--- a/x/payment/types/price.go
+++ b/x/payment/types/price.go
@@ -37,7 +37,7 @@ func (change *StreamRecordChange) WithLockBalanceChange(lockBalanceChange sdkmat
 }
 
 type StoragePriceParams struct {
-	PrimarySp string
+	PrimarySp uint32
 	PriceTime int64
 }
 
diff --git a/x/payment/types/query.pb.go b/x/payment/types/query.pb.go
index 3c4a0c385..ebcbb96ca 100644
--- a/x/payment/types/query.pb.go
+++ b/x/payment/types/query.pb.go
@@ -207,6 +207,94 @@ func (m *QueryParamsByTimestampResponse) GetParams() Params {
 	return Params{}
 }
 
+type QueryOutFlowsRequest struct {
+	Account string `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"`
+}
+
+func (m *QueryOutFlowsRequest) Reset()         { *m = QueryOutFlowsRequest{} }
+func (m *QueryOutFlowsRequest) String() string { return proto.CompactTextString(m) }
+func (*QueryOutFlowsRequest) ProtoMessage()    {}
+func (*QueryOutFlowsRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_f62e6684473ccf4a, []int{4}
+}
+func (m *QueryOutFlowsRequest) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *QueryOutFlowsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_QueryOutFlowsRequest.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *QueryOutFlowsRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_QueryOutFlowsRequest.Merge(m, src)
+}
+func (m *QueryOutFlowsRequest) XXX_Size() int {
+	return m.Size()
+}
+func (m *QueryOutFlowsRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_QueryOutFlowsRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryOutFlowsRequest proto.InternalMessageInfo
+
+func (m *QueryOutFlowsRequest) GetAccount() string {
+	if m != nil {
+		return m.Account
+	}
+	return ""
+}
+
+type QueryOutFlowsResponse struct {
+	OutFlows []OutFlow `protobuf:"bytes,1,rep,name=out_flows,json=outFlows,proto3" json:"out_flows"`
+}
+
+func (m *QueryOutFlowsResponse) Reset()         { *m = QueryOutFlowsResponse{} }
+func (m *QueryOutFlowsResponse) String() string { return proto.CompactTextString(m) }
+func (*QueryOutFlowsResponse) ProtoMessage()    {}
+func (*QueryOutFlowsResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_f62e6684473ccf4a, []int{5}
+}
+func (m *QueryOutFlowsResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *QueryOutFlowsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_QueryOutFlowsResponse.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *QueryOutFlowsResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_QueryOutFlowsResponse.Merge(m, src)
+}
+func (m *QueryOutFlowsResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *QueryOutFlowsResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_QueryOutFlowsResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryOutFlowsResponse proto.InternalMessageInfo
+
+func (m *QueryOutFlowsResponse) GetOutFlows() []OutFlow {
+	if m != nil {
+		return m.OutFlows
+	}
+	return nil
+}
+
 type QueryGetStreamRecordRequest struct {
 	Account string `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"`
 }
@@ -215,7 +303,7 @@ func (m *QueryGetStreamRecordRequest) Reset()         { *m = QueryGetStreamRecor
 func (m *QueryGetStreamRecordRequest) String() string { return proto.CompactTextString(m) }
 func (*QueryGetStreamRecordRequest) ProtoMessage()    {}
 func (*QueryGetStreamRecordRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_f62e6684473ccf4a, []int{4}
+	return fileDescriptor_f62e6684473ccf4a, []int{6}
 }
 func (m *QueryGetStreamRecordRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -259,7 +347,7 @@ func (m *QueryGetStreamRecordResponse) Reset()         { *m = QueryGetStreamReco
 func (m *QueryGetStreamRecordResponse) String() string { return proto.CompactTextString(m) }
 func (*QueryGetStreamRecordResponse) ProtoMessage()    {}
 func (*QueryGetStreamRecordResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_f62e6684473ccf4a, []int{5}
+	return fileDescriptor_f62e6684473ccf4a, []int{7}
 }
 func (m *QueryGetStreamRecordResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -303,7 +391,7 @@ func (m *QueryAllStreamRecordRequest) Reset()         { *m = QueryAllStreamRecor
 func (m *QueryAllStreamRecordRequest) String() string { return proto.CompactTextString(m) }
 func (*QueryAllStreamRecordRequest) ProtoMessage()    {}
 func (*QueryAllStreamRecordRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_f62e6684473ccf4a, []int{6}
+	return fileDescriptor_f62e6684473ccf4a, []int{8}
 }
 func (m *QueryAllStreamRecordRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -348,7 +436,7 @@ func (m *QueryAllStreamRecordResponse) Reset()         { *m = QueryAllStreamReco
 func (m *QueryAllStreamRecordResponse) String() string { return proto.CompactTextString(m) }
 func (*QueryAllStreamRecordResponse) ProtoMessage()    {}
 func (*QueryAllStreamRecordResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_f62e6684473ccf4a, []int{7}
+	return fileDescriptor_f62e6684473ccf4a, []int{9}
 }
 func (m *QueryAllStreamRecordResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -399,7 +487,7 @@ func (m *QueryGetPaymentAccountCountRequest) Reset()         { *m = QueryGetPaym
 func (m *QueryGetPaymentAccountCountRequest) String() string { return proto.CompactTextString(m) }
 func (*QueryGetPaymentAccountCountRequest) ProtoMessage()    {}
 func (*QueryGetPaymentAccountCountRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_f62e6684473ccf4a, []int{8}
+	return fileDescriptor_f62e6684473ccf4a, []int{10}
 }
 func (m *QueryGetPaymentAccountCountRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -443,7 +531,7 @@ func (m *QueryGetPaymentAccountCountResponse) Reset()         { *m = QueryGetPay
 func (m *QueryGetPaymentAccountCountResponse) String() string { return proto.CompactTextString(m) }
 func (*QueryGetPaymentAccountCountResponse) ProtoMessage()    {}
 func (*QueryGetPaymentAccountCountResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_f62e6684473ccf4a, []int{9}
+	return fileDescriptor_f62e6684473ccf4a, []int{11}
 }
 func (m *QueryGetPaymentAccountCountResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -487,7 +575,7 @@ func (m *QueryAllPaymentAccountCountRequest) Reset()         { *m = QueryAllPaym
 func (m *QueryAllPaymentAccountCountRequest) String() string { return proto.CompactTextString(m) }
 func (*QueryAllPaymentAccountCountRequest) ProtoMessage()    {}
 func (*QueryAllPaymentAccountCountRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_f62e6684473ccf4a, []int{10}
+	return fileDescriptor_f62e6684473ccf4a, []int{12}
 }
 func (m *QueryAllPaymentAccountCountRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -532,7 +620,7 @@ func (m *QueryAllPaymentAccountCountResponse) Reset()         { *m = QueryAllPay
 func (m *QueryAllPaymentAccountCountResponse) String() string { return proto.CompactTextString(m) }
 func (*QueryAllPaymentAccountCountResponse) ProtoMessage()    {}
 func (*QueryAllPaymentAccountCountResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_f62e6684473ccf4a, []int{11}
+	return fileDescriptor_f62e6684473ccf4a, []int{13}
 }
 func (m *QueryAllPaymentAccountCountResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -583,7 +671,7 @@ func (m *QueryGetPaymentAccountRequest) Reset()         { *m = QueryGetPaymentAc
 func (m *QueryGetPaymentAccountRequest) String() string { return proto.CompactTextString(m) }
 func (*QueryGetPaymentAccountRequest) ProtoMessage()    {}
 func (*QueryGetPaymentAccountRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_f62e6684473ccf4a, []int{12}
+	return fileDescriptor_f62e6684473ccf4a, []int{14}
 }
 func (m *QueryGetPaymentAccountRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -627,7 +715,7 @@ func (m *QueryGetPaymentAccountResponse) Reset()         { *m = QueryGetPaymentA
 func (m *QueryGetPaymentAccountResponse) String() string { return proto.CompactTextString(m) }
 func (*QueryGetPaymentAccountResponse) ProtoMessage()    {}
 func (*QueryGetPaymentAccountResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_f62e6684473ccf4a, []int{13}
+	return fileDescriptor_f62e6684473ccf4a, []int{15}
 }
 func (m *QueryGetPaymentAccountResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -671,7 +759,7 @@ func (m *QueryAllPaymentAccountRequest) Reset()         { *m = QueryAllPaymentAc
 func (m *QueryAllPaymentAccountRequest) String() string { return proto.CompactTextString(m) }
 func (*QueryAllPaymentAccountRequest) ProtoMessage()    {}
 func (*QueryAllPaymentAccountRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_f62e6684473ccf4a, []int{14}
+	return fileDescriptor_f62e6684473ccf4a, []int{16}
 }
 func (m *QueryAllPaymentAccountRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -716,7 +804,7 @@ func (m *QueryAllPaymentAccountResponse) Reset()         { *m = QueryAllPaymentA
 func (m *QueryAllPaymentAccountResponse) String() string { return proto.CompactTextString(m) }
 func (*QueryAllPaymentAccountResponse) ProtoMessage()    {}
 func (*QueryAllPaymentAccountResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_f62e6684473ccf4a, []int{15}
+	return fileDescriptor_f62e6684473ccf4a, []int{17}
 }
 func (m *QueryAllPaymentAccountResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -767,7 +855,7 @@ func (m *QueryDynamicBalanceRequest) Reset()         { *m = QueryDynamicBalanceR
 func (m *QueryDynamicBalanceRequest) String() string { return proto.CompactTextString(m) }
 func (*QueryDynamicBalanceRequest) ProtoMessage()    {}
 func (*QueryDynamicBalanceRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_f62e6684473ccf4a, []int{16}
+	return fileDescriptor_f62e6684473ccf4a, []int{18}
 }
 func (m *QueryDynamicBalanceRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -824,7 +912,7 @@ func (m *QueryDynamicBalanceResponse) Reset()         { *m = QueryDynamicBalance
 func (m *QueryDynamicBalanceResponse) String() string { return proto.CompactTextString(m) }
 func (*QueryDynamicBalanceResponse) ProtoMessage()    {}
 func (*QueryDynamicBalanceResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_f62e6684473ccf4a, []int{17}
+	return fileDescriptor_f62e6684473ccf4a, []int{19}
 }
 func (m *QueryDynamicBalanceResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -875,7 +963,7 @@ func (m *QueryGetPaymentAccountsByOwnerRequest) Reset()         { *m = QueryGetP
 func (m *QueryGetPaymentAccountsByOwnerRequest) String() string { return proto.CompactTextString(m) }
 func (*QueryGetPaymentAccountsByOwnerRequest) ProtoMessage()    {}
 func (*QueryGetPaymentAccountsByOwnerRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_f62e6684473ccf4a, []int{18}
+	return fileDescriptor_f62e6684473ccf4a, []int{20}
 }
 func (m *QueryGetPaymentAccountsByOwnerRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -921,7 +1009,7 @@ func (m *QueryGetPaymentAccountsByOwnerResponse) Reset() {
 func (m *QueryGetPaymentAccountsByOwnerResponse) String() string { return proto.CompactTextString(m) }
 func (*QueryGetPaymentAccountsByOwnerResponse) ProtoMessage()    {}
 func (*QueryGetPaymentAccountsByOwnerResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_f62e6684473ccf4a, []int{19}
+	return fileDescriptor_f62e6684473ccf4a, []int{21}
 }
 func (m *QueryGetPaymentAccountsByOwnerResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -965,7 +1053,7 @@ func (m *QueryAllAutoSettleRecordRequest) Reset()         { *m = QueryAllAutoSet
 func (m *QueryAllAutoSettleRecordRequest) String() string { return proto.CompactTextString(m) }
 func (*QueryAllAutoSettleRecordRequest) ProtoMessage()    {}
 func (*QueryAllAutoSettleRecordRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_f62e6684473ccf4a, []int{20}
+	return fileDescriptor_f62e6684473ccf4a, []int{22}
 }
 func (m *QueryAllAutoSettleRecordRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -1010,7 +1098,7 @@ func (m *QueryAllAutoSettleRecordResponse) Reset()         { *m = QueryAllAutoSe
 func (m *QueryAllAutoSettleRecordResponse) String() string { return proto.CompactTextString(m) }
 func (*QueryAllAutoSettleRecordResponse) ProtoMessage()    {}
 func (*QueryAllAutoSettleRecordResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_f62e6684473ccf4a, []int{21}
+	return fileDescriptor_f62e6684473ccf4a, []int{23}
 }
 func (m *QueryAllAutoSettleRecordResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -1058,6 +1146,8 @@ func init() {
 	proto.RegisterType((*QueryParamsResponse)(nil), "greenfield.payment.QueryParamsResponse")
 	proto.RegisterType((*QueryParamsByTimestampRequest)(nil), "greenfield.payment.QueryParamsByTimestampRequest")
 	proto.RegisterType((*QueryParamsByTimestampResponse)(nil), "greenfield.payment.QueryParamsByTimestampResponse")
+	proto.RegisterType((*QueryOutFlowsRequest)(nil), "greenfield.payment.QueryOutFlowsRequest")
+	proto.RegisterType((*QueryOutFlowsResponse)(nil), "greenfield.payment.QueryOutFlowsResponse")
 	proto.RegisterType((*QueryGetStreamRecordRequest)(nil), "greenfield.payment.QueryGetStreamRecordRequest")
 	proto.RegisterType((*QueryGetStreamRecordResponse)(nil), "greenfield.payment.QueryGetStreamRecordResponse")
 	proto.RegisterType((*QueryAllStreamRecordRequest)(nil), "greenfield.payment.QueryAllStreamRecordRequest")
@@ -1081,86 +1171,91 @@ func init() {
 func init() { proto.RegisterFile("greenfield/payment/query.proto", fileDescriptor_f62e6684473ccf4a) }
 
 var fileDescriptor_f62e6684473ccf4a = []byte{
-	// 1253 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0xcd, 0x6f, 0x1b, 0x45,
-	0x14, 0xcf, 0x36, 0x1f, 0x55, 0x5e, 0x42, 0x3e, 0x26, 0x01, 0xa5, 0x26, 0x38, 0x61, 0x43, 0x9d,
-	0xb4, 0x21, 0x5e, 0x9c, 0x40, 0x4a, 0x0a, 0x3d, 0xd8, 0xa0, 0x56, 0x11, 0x87, 0xa6, 0x2e, 0x07,
-	0x54, 0x84, 0x56, 0xe3, 0xf5, 0x74, 0xb3, 0xca, 0x7a, 0xd7, 0xdd, 0x1d, 0x17, 0xac, 0x28, 0x17,
-	0x0e, 0x88, 0x1b, 0x48, 0xfc, 0x01, 0xdc, 0x00, 0xc1, 0x85, 0x43, 0x8f, 0xbd, 0x70, 0x40, 0xea,
-	0xb1, 0x80, 0x90, 0x10, 0x87, 0x0a, 0x25, 0xfc, 0x21, 0x68, 0x67, 0xdf, 0x3a, 0x6b, 0xef, 0x97,
-	0x93, 0x9a, 0x4b, 0xec, 0x9d, 0x7d, 0x1f, 0xbf, 0xdf, 0x9b, 0x79, 0x6f, 0x7e, 0x0e, 0xe4, 0x75,
-	0x87, 0x31, 0xeb, 0xbe, 0xc1, 0xcc, 0xba, 0xd2, 0xa4, 0xed, 0x06, 0xb3, 0xb8, 0xf2, 0xa0, 0xc5,
-	0x9c, 0x76, 0xb1, 0xe9, 0xd8, 0xdc, 0x26, 0xe4, 0xf4, 0x7d, 0x11, 0xdf, 0xe7, 0xae, 0x6a, 0xb6,
-	0xdb, 0xb0, 0x5d, 0xa5, 0x46, 0x5d, 0xe6, 0x1b, 0x2b, 0x0f, 0x4b, 0x35, 0xc6, 0x69, 0x49, 0x69,
-	0x52, 0xdd, 0xb0, 0x28, 0x37, 0x6c, 0xcb, 0xf7, 0xcf, 0x5d, 0xf2, 0x6d, 0x55, 0xf1, 0xa4, 0xf8,
-	0x0f, 0xf8, 0x6a, 0x5e, 0xb7, 0x75, 0xdb, 0x5f, 0xf7, 0xbe, 0xe1, 0xea, 0xa2, 0x6e, 0xdb, 0xba,
-	0xc9, 0x14, 0xda, 0x34, 0x14, 0x6a, 0x59, 0x36, 0x17, 0xd1, 0x02, 0x9f, 0xf5, 0x18, 0xb8, 0xb4,
-	0xc5, 0x6d, 0xd5, 0x65, 0x9c, 0x9b, 0x4c, 0x75, 0x98, 0x66, 0x3b, 0x75, 0x34, 0x5e, 0x8a, 0x31,
-	0x6e, 0x52, 0x87, 0x36, 0x82, 0x68, 0x6b, 0xb1, 0x06, 0xe2, 0x53, 0xa5, 0x9a, 0x66, 0xb7, 0x2c,
-	0x8e, 0x96, 0xc5, 0x6c, 0x4b, 0x35, 0x6c, 0x5f, 0x88, 0xb1, 0x77, 0xb9, 0xc3, 0x68, 0xa3, 0x0b,
-	0xa2, 0x3c, 0x0f, 0xe4, 0x8e, 0x57, 0xc0, 0x3d, 0x01, 0xab, 0xca, 0x1e, 0xb4, 0x98, 0xcb, 0xe5,
-	0xdb, 0x30, 0xd7, 0xb5, 0xea, 0x36, 0x6d, 0xcb, 0x65, 0xe4, 0x6d, 0x18, 0xf3, 0xe1, 0x2f, 0x48,
-	0xcb, 0xd2, 0xda, 0xc4, 0x66, 0x18, 0x55, 0xb0, 0x39, 0x45, 0xdf, 0xa7, 0x32, 0xf2, 0xe4, 0xd9,
-	0xd2, 0x50, 0x15, 0xed, 0xe5, 0x1b, 0xf0, 0x4a, 0x28, 0x60, 0xa5, 0xfd, 0xa1, 0xd1, 0x60, 0x2e,
-	0xa7, 0x8d, 0x26, 0x66, 0x24, 0x8b, 0x30, 0xce, 0x83, 0x35, 0x11, 0x7d, 0xb8, 0x7a, 0xba, 0x20,
-	0xdf, 0x83, 0x7c, 0x92, 0xfb, 0x73, 0x43, 0xbb, 0x06, 0x2f, 0x8b, 0xd8, 0xb7, 0x18, 0xbf, 0x2b,
-	0x0a, 0x54, 0x15, 0xf5, 0x09, 0x80, 0x2d, 0xc0, 0x45, 0xac, 0xaf, 0x88, 0x3c, 0x5e, 0x0d, 0x1e,
-	0xe5, 0x03, 0x58, 0x8c, 0x77, 0x44, 0x48, 0x1f, 0xc0, 0x0b, 0x5d, 0x15, 0x47, 0x64, 0xcb, 0x71,
-	0xc8, 0xc2, 0x01, 0x10, 0xdf, 0xa4, 0x1b, 0x5a, 0x93, 0x19, 0xa2, 0x2c, 0x9b, 0x66, 0x1c, 0xca,
-	0x9b, 0x00, 0xa7, 0x27, 0x1f, 0x13, 0x15, 0x8a, 0x78, 0xda, 0xbd, 0x36, 0x29, 0xfa, 0x3d, 0x85,
-	0x6d, 0x52, 0xdc, 0xa3, 0x3a, 0x43, 0xdf, 0x6a, 0xc8, 0x53, 0x7e, 0x24, 0x21, 0xa9, 0x48, 0x9e,
-	0x64, 0x52, 0xc3, 0xe7, 0x25, 0x45, 0x6e, 0x75, 0xa1, 0xbe, 0x20, 0x50, 0xaf, 0x66, 0xa2, 0xf6,
-	0x91, 0x74, 0xc1, 0xbe, 0x0e, 0x72, 0xb0, 0x15, 0x7b, 0x7e, 0xf2, 0xb2, 0xbf, 0x49, 0xef, 0x79,
-	0x7f, 0x82, 0x22, 0xcd, 0xc3, 0xa8, 0xfd, 0xa9, 0xc5, 0x1c, 0xdc, 0x48, 0xff, 0x41, 0xfe, 0x52,
-	0x82, 0x95, 0x54, 0x67, 0x64, 0x4e, 0xe1, 0xc5, 0xd8, 0x86, 0xc3, 0x6a, 0xaf, 0xc6, 0x1f, 0xb8,
-	0x48, 0x3c, 0x2c, 0xc4, 0x5c, 0x33, 0xfa, 0x4a, 0x36, 0x91, 0x46, 0xd9, 0x34, 0x53, 0x68, 0x0c,
-	0x6a, 0xaf, 0x7f, 0x0b, 0x88, 0x27, 0xa5, 0xcb, 0x26, 0x3e, 0x3c, 0x18, 0xe2, 0x83, 0x3b, 0x08,
-	0x5b, 0x38, 0x67, 0x22, 0x7b, 0x19, 0x14, 0x8f, 0xc0, 0x08, 0xad, 0xd7, 0x83, 0x23, 0x20, 0xbe,
-	0xcb, 0x2e, 0x4e, 0x97, 0x18, 0x27, 0x2c, 0xc1, 0x1d, 0x98, 0xee, 0x29, 0x01, 0xd6, 0x5d, 0xce,
-	0x26, 0x8f, 0xbc, 0xa7, 0xba, 0x79, 0xcb, 0x3a, 0x22, 0x8d, 0x14, 0x7f, 0xd0, 0xdb, 0xfc, 0x58,
-	0x42, 0x7a, 0x31, 0x99, 0xd2, 0xe8, 0x0d, 0x3f, 0x0f, 0xbd, 0xc1, 0xed, 0xe8, 0x36, 0xe4, 0x04,
-	0xfa, 0xf7, 0xdb, 0x16, 0x6d, 0x18, 0x5a, 0x85, 0x9a, 0xd4, 0xd2, 0x58, 0xf6, 0x74, 0xfe, 0x62,
-	0x14, 0x27, 0x66, 0xaf, 0x23, 0x72, 0x66, 0x30, 0x5d, 0xf7, 0xdf, 0xa8, 0x35, 0xff, 0x95, 0x1f,
-	0xa1, 0xf2, 0xae, 0xc7, 0xe7, 0xef, 0x67, 0x4b, 0x05, 0xdd, 0xe0, 0xfb, 0xad, 0x5a, 0x51, 0xb3,
-	0x1b, 0x28, 0x1b, 0xf0, 0x63, 0xc3, 0xad, 0x1f, 0x28, 0xbc, 0xdd, 0x64, 0x6e, 0x71, 0xd7, 0xe2,
-	0xbf, 0x3f, 0xda, 0x00, 0xa4, 0xb5, 0x6b, 0xf1, 0xea, 0x54, 0xbd, 0x2b, 0x5d, 0x74, 0x5e, 0x5e,
-	0x38, 0xff, 0x25, 0x40, 0xd6, 0x61, 0x56, 0x6b, 0x39, 0x8e, 0xb7, 0x4f, 0xa7, 0x97, 0xe5, 0xb0,
-	0xb8, 0x2c, 0x67, 0xf0, 0x45, 0xe7, 0x66, 0x24, 0x2a, 0x4c, 0xd6, 0xa8, 0x75, 0xd0, 0x61, 0x37,
-	0x32, 0x00, 0x76, 0x13, 0x5e, 0xc4, 0x80, 0x9a, 0x01, 0xb3, 0xf4, 0x21, 0x35, 0x4c, 0x5a, 0x33,
-	0x59, 0x27, 0xcb, 0xe8, 0x00, 0xb2, 0xcc, 0x74, 0xc2, 0x06, 0xa9, 0x3e, 0x06, 0x30, 0x6d, 0xed,
-	0x80, 0xd5, 0xd5, 0xfb, 0x8c, 0x2d, 0x8c, 0x0d, 0x20, 0xc7, 0xb8, 0x1f, 0xef, 0x26, 0x63, 0xe4,
-	0x13, 0x98, 0xd0, 0xf6, 0xa9, 0xa5, 0x33, 0xd5, 0xa1, 0x9c, 0x2d, 0x5c, 0x1c, 0x40, 0x74, 0xf0,
-	0x03, 0x56, 0x29, 0x67, 0xf2, 0x0d, 0xb8, 0x1c, 0x3f, 0x5d, 0xdc, 0x4a, 0xfb, 0xb6, 0x77, 0x03,
-	0xa5, 0x5f, 0x4f, 0x55, 0x28, 0x64, 0xb9, 0xe3, 0x89, 0x5e, 0xeb, 0x74, 0x71, 0x60, 0x21, 0xba,
-	0x78, 0xbc, 0xda, 0xbb, 0x2c, 0x1b, 0xb0, 0x14, 0x4c, 0x84, 0x72, 0x8b, 0xdb, 0x77, 0x85, 0x74,
-	0xfd, 0x7f, 0x04, 0xc5, 0xaf, 0x12, 0x2c, 0x27, 0xe7, 0x42, 0xe4, 0x1f, 0x01, 0x89, 0x6a, 0x68,
-	0x1c, 0x41, 0xaf, 0xc5, 0x75, 0x4a, 0x6f, 0x24, 0xec, 0x96, 0x19, 0xda, 0xb3, 0x3e, 0xb0, 0x31,
-	0xb4, 0xf9, 0xd5, 0x34, 0x8c, 0x0a, 0x1e, 0xe4, 0x08, 0xc6, 0x7c, 0x1d, 0x49, 0x0a, 0x71, 0xd0,
-	0xa2, 0x6a, 0x3a, 0xb7, 0x9a, 0x69, 0xe7, 0x27, 0x94, 0xe5, 0xcf, 0xff, 0xf8, 0xf7, 0x9b, 0x0b,
-	0x8b, 0x24, 0xa7, 0x24, 0xfe, 0x70, 0x20, 0x3f, 0x4a, 0x30, 0x1b, 0x91, 0xc1, 0xa4, 0x94, 0x91,
-	0x22, 0xaa, 0xb8, 0x73, 0x9b, 0x67, 0x71, 0x41, 0x80, 0x45, 0x01, 0x70, 0x8d, 0x14, 0x92, 0x01,
-	0x2a, 0x87, 0x9d, 0xd1, 0x74, 0x44, 0xbe, 0x93, 0x60, 0x32, 0x3c, 0xd5, 0x88, 0x92, 0x98, 0x34,
-	0x5e, 0x7e, 0xe7, 0xde, 0xe8, 0xdf, 0x01, 0x31, 0x6e, 0x09, 0x8c, 0x1b, 0x64, 0x5d, 0xc9, 0xfa,
-	0x09, 0xa4, 0x1c, 0xe2, 0x65, 0x71, 0x44, 0xbe, 0x95, 0x60, 0x3a, 0x1c, 0xad, 0x6c, 0x9a, 0x29,
-	0x58, 0xe3, 0x45, 0x78, 0x0a, 0xd6, 0x04, 0x35, 0x2d, 0x5f, 0x11, 0x58, 0x57, 0xc8, 0xab, 0x99,
-	0x58, 0xc9, 0x2f, 0x12, 0xcc, 0xc5, 0xa8, 0x2a, 0xb2, 0x9d, 0x56, 0xa0, 0x64, 0x15, 0x99, 0xbb,
-	0x76, 0x66, 0x3f, 0xc4, 0xbc, 0x23, 0x30, 0x6f, 0x91, 0x92, 0xd2, 0xef, 0x4f, 0x52, 0xe5, 0x50,
-	0x8c, 0xb2, 0x23, 0xf2, 0x58, 0x82, 0x97, 0x62, 0x42, 0x7b, 0xc5, 0xde, 0x4e, 0xab, 0xdd, 0xb9,
-	0x68, 0xa4, 0xab, 0x5a, 0xb9, 0x24, 0x68, 0xac, 0x93, 0x2b, 0x7d, 0xd3, 0x20, 0x3f, 0x49, 0x30,
-	0xd5, 0x1d, 0x32, 0xa5, 0xef, 0x92, 0x14, 0x68, 0x4a, 0xdf, 0x25, 0xea, 0x4f, 0x79, 0x53, 0x80,
-	0x7d, 0x9d, 0x5c, 0xed, 0x03, 0xac, 0x72, 0xe8, 0x89, 0xda, 0x23, 0xf2, 0xbd, 0x18, 0x14, 0xe1,
-	0x70, 0x5e, 0x9d, 0x4b, 0xfd, 0xd7, 0x2b, 0x1b, 0x70, 0xa2, 0xa2, 0x94, 0xd7, 0x05, 0xe0, 0xcb,
-	0x64, 0xa5, 0x0f, 0xc0, 0xe4, 0x07, 0x09, 0xa6, 0xba, 0x55, 0x1a, 0x29, 0x26, 0xe6, 0x8c, 0xd5,
-	0x81, 0x39, 0xa5, 0x6f, 0x7b, 0x04, 0xf8, 0x96, 0x00, 0xa8, 0x90, 0x8d, 0x38, 0x80, 0x3d, 0xc2,
-	0x30, 0x34, 0x27, 0xfe, 0x94, 0xe0, 0x52, 0xe2, 0x4d, 0x4c, 0x76, 0xfa, 0xdf, 0xda, 0x9e, 0xcb,
-	0x3f, 0x77, 0xfd, 0x3c, 0xae, 0xc8, 0xa5, 0x2c, 0xb8, 0xbc, 0x43, 0x76, 0xe2, 0xb8, 0xe8, 0x8c,
-	0xab, 0x3d, 0x05, 0x77, 0xd5, 0x5a, 0x5b, 0x15, 0x2d, 0xd9, 0xe9, 0xcc, 0x9f, 0x25, 0x98, 0xeb,
-	0xbd, 0x54, 0xbd, 0xe3, 0xb2, 0x95, 0xb6, 0xf7, 0x09, 0xda, 0x21, 0xf7, 0xe6, 0xd9, 0x9c, 0xfa,
-	0xb9, 0x5b, 0xa2, 0xf2, 0xa0, 0xb2, 0xfb, 0xe4, 0x38, 0x2f, 0x3d, 0x3d, 0xce, 0x4b, 0xff, 0x1c,
-	0xe7, 0xa5, 0xaf, 0x4f, 0xf2, 0x43, 0x4f, 0x4f, 0xf2, 0x43, 0x7f, 0x9d, 0xe4, 0x87, 0xee, 0x29,
-	0x21, 0xcd, 0x56, 0xb3, 0x6a, 0x1b, 0xda, 0x3e, 0x35, 0xac, 0x70, 0xd4, 0xcf, 0x3a, 0x71, 0x85,
-	0x80, 0xab, 0x8d, 0x89, 0xff, 0x85, 0x6d, 0xfd, 0x17, 0x00, 0x00, 0xff, 0xff, 0x2b, 0xc3, 0x40,
-	0xb9, 0x8c, 0x14, 0x00, 0x00,
+	// 1341 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0xcf, 0x6f, 0x1b, 0x45,
+	0x14, 0xce, 0xd6, 0x4d, 0xda, 0xbc, 0x94, 0x34, 0x9d, 0xa4, 0x28, 0xdd, 0x06, 0xa7, 0xdd, 0x52,
+	0xc7, 0x69, 0x88, 0xb7, 0x4e, 0x20, 0x25, 0x85, 0x22, 0xd9, 0xa0, 0x54, 0x11, 0x87, 0xa4, 0x2e,
+	0x12, 0xa8, 0x08, 0xad, 0xc6, 0xf6, 0xc4, 0xb1, 0xb2, 0xde, 0x75, 0xbd, 0xe3, 0x06, 0x2b, 0xca,
+	0x85, 0x03, 0xe2, 0x58, 0x89, 0x3f, 0x00, 0x89, 0x03, 0x20, 0xb8, 0x70, 0xe8, 0xb1, 0x17, 0x0e,
+	0x48, 0x3d, 0x16, 0x10, 0x12, 0xe2, 0x50, 0xa1, 0x84, 0x3f, 0x04, 0xed, 0xec, 0x5b, 0x67, 0x6d,
+	0xcf, 0xee, 0x3a, 0xa9, 0xb9, 0x34, 0xde, 0xd9, 0xf7, 0xe3, 0xfb, 0xde, 0xdb, 0x79, 0xf3, 0x4d,
+	0x21, 0x59, 0x69, 0x30, 0x66, 0x6d, 0x55, 0x99, 0x59, 0xd6, 0xeb, 0xb4, 0x55, 0x63, 0x16, 0xd7,
+	0x1f, 0x36, 0x59, 0xa3, 0x95, 0xa9, 0x37, 0x6c, 0x6e, 0x13, 0x72, 0xf4, 0x3e, 0x83, 0xef, 0xd5,
+	0x1b, 0x25, 0xdb, 0xa9, 0xd9, 0x8e, 0x5e, 0xa4, 0x0e, 0xf3, 0x8c, 0xf5, 0x47, 0xd9, 0x22, 0xe3,
+	0x34, 0xab, 0xd7, 0x69, 0xa5, 0x6a, 0x51, 0x5e, 0xb5, 0x2d, 0xcf, 0x5f, 0xbd, 0xe4, 0xd9, 0x1a,
+	0xe2, 0x49, 0xf7, 0x1e, 0xf0, 0xd5, 0x54, 0xc5, 0xae, 0xd8, 0xde, 0xba, 0xfb, 0x0b, 0x57, 0x67,
+	0x2a, 0xb6, 0x5d, 0x31, 0x99, 0x4e, 0xeb, 0x55, 0x9d, 0x5a, 0x96, 0xcd, 0x45, 0x34, 0xdf, 0x67,
+	0x41, 0x02, 0x97, 0x36, 0xb9, 0x6d, 0x38, 0x8c, 0x73, 0x93, 0x19, 0x0d, 0x56, 0xb2, 0x1b, 0x65,
+	0x34, 0xbe, 0x2a, 0x31, 0xb6, 0x9b, 0xdc, 0xd8, 0x32, 0xed, 0x5d, 0x34, 0x99, 0x95, 0x98, 0xd4,
+	0x69, 0x83, 0xd6, 0xfc, 0x84, 0x69, 0xa9, 0x81, 0xf8, 0x6b, 0xd0, 0x52, 0xc9, 0x6e, 0x5a, 0x1c,
+	0x2d, 0x33, 0xf1, 0x96, 0x46, 0xd0, 0x3e, 0x25, 0xb1, 0x77, 0x78, 0x83, 0xd1, 0x5a, 0x07, 0x0b,
+	0x6d, 0x0a, 0xc8, 0x3d, 0xb7, 0xc6, 0x9b, 0x02, 0x56, 0x81, 0x3d, 0x6c, 0x32, 0x87, 0x6b, 0x1b,
+	0x30, 0xd9, 0xb1, 0xea, 0xd4, 0x6d, 0xcb, 0x61, 0xe4, 0x6d, 0x18, 0xf1, 0xe0, 0x4f, 0x2b, 0x57,
+	0x94, 0xf4, 0xd8, 0x52, 0x10, 0x95, 0xdf, 0xbf, 0x8c, 0xe7, 0x93, 0x3f, 0xfd, 0xec, 0xc5, 0xec,
+	0x50, 0x01, 0xed, 0xb5, 0x3b, 0xf0, 0x5a, 0x20, 0x60, 0xbe, 0xf5, 0x51, 0xb5, 0xc6, 0x1c, 0x4e,
+	0x6b, 0x75, 0xcc, 0x48, 0x66, 0x60, 0x94, 0xfb, 0x6b, 0x22, 0x7a, 0xa2, 0x70, 0xb4, 0xa0, 0x3d,
+	0x80, 0x64, 0x98, 0xfb, 0x4b, 0x43, 0xbb, 0x09, 0x53, 0x22, 0xf6, 0x46, 0x93, 0xaf, 0x99, 0xf6,
+	0xae, 0x5f, 0x03, 0x32, 0x0d, 0x67, 0xb0, 0xb0, 0x22, 0xe4, 0x68, 0xc1, 0x7f, 0xd4, 0x3e, 0x86,
+	0x8b, 0x5d, 0x1e, 0x08, 0xe2, 0x3d, 0x18, 0xf5, 0xbf, 0x00, 0x17, 0x47, 0x22, 0x3d, 0xb6, 0x74,
+	0x59, 0x86, 0x03, 0x1d, 0x11, 0xc8, 0x59, 0x1b, 0xe3, 0x68, 0xb7, 0xe0, 0xb2, 0x08, 0x7c, 0x97,
+	0xf1, 0xfb, 0xa2, 0x57, 0x05, 0xd1, 0xaa, 0x78, 0x44, 0x3b, 0x30, 0x23, 0x77, 0x44, 0x60, 0x1f,
+	0xc2, 0x2b, 0x1d, 0xcd, 0xc7, 0x22, 0x5d, 0x91, 0x81, 0x0b, 0x06, 0x40, 0x84, 0xe7, 0x9c, 0xc0,
+	0x9a, 0xc6, 0x10, 0x65, 0xce, 0x34, 0x65, 0x28, 0xd7, 0x00, 0x8e, 0xf6, 0x29, 0x26, 0x4a, 0x65,
+	0x70, 0x6f, 0xba, 0x9b, 0x3a, 0xe3, 0x4d, 0x00, 0xdc, 0xd4, 0x99, 0x4d, 0x5a, 0x61, 0xe8, 0x5b,
+	0x08, 0x78, 0x6a, 0x4f, 0x14, 0x24, 0xd5, 0x93, 0x27, 0x9c, 0x54, 0xe2, 0xa4, 0xa4, 0xc8, 0xdd,
+	0x0e, 0xd4, 0xa7, 0x04, 0xea, 0xb9, 0x58, 0xd4, 0x1e, 0x92, 0x0e, 0xd8, 0xb7, 0x41, 0xf3, 0x5b,
+	0xb1, 0xe9, 0x25, 0xcf, 0x79, 0x4d, 0x7a, 0xdf, 0xfd, 0xc7, 0x2f, 0xd2, 0x14, 0x0c, 0xdb, 0xbb,
+	0x16, 0x6b, 0x60, 0x23, 0xbd, 0x07, 0xed, 0x2b, 0x05, 0xae, 0x45, 0x3a, 0x23, 0x73, 0x0a, 0x17,
+	0xa5, 0x7b, 0x1f, 0xab, 0x3d, 0x27, 0xff, 0xf6, 0x7b, 0xe2, 0x61, 0x21, 0x26, 0xeb, 0xbd, 0xaf,
+	0x34, 0x13, 0x69, 0xe4, 0x4c, 0x33, 0x82, 0xc6, 0xa0, 0x7a, 0xfd, 0x9b, 0x4f, 0x3c, 0x2c, 0x5d,
+	0x3c, 0xf1, 0xc4, 0x60, 0x88, 0x0f, 0xee, 0x43, 0x58, 0xc6, 0x91, 0xd7, 0xd3, 0x4b, 0xbf, 0x78,
+	0x04, 0x4e, 0xd3, 0x72, 0xd9, 0xff, 0x04, 0xc4, 0x6f, 0xcd, 0xc1, 0x41, 0x27, 0x71, 0xc2, 0x12,
+	0xdc, 0x83, 0xf3, 0x5d, 0x25, 0xc0, 0xba, 0x6b, 0xf1, 0xe4, 0x91, 0xf7, 0x78, 0x27, 0x6f, 0xad,
+	0x82, 0x48, 0x7b, 0x8a, 0x3f, 0xe8, 0x36, 0x3f, 0x55, 0x90, 0x9e, 0x24, 0x53, 0x14, 0xbd, 0xc4,
+	0xcb, 0xd0, 0x1b, 0x5c, 0x47, 0x57, 0x40, 0x15, 0xe8, 0x3f, 0x68, 0x59, 0xb4, 0x56, 0x2d, 0xe5,
+	0xa9, 0x49, 0xad, 0x12, 0x8b, 0x9f, 0xce, 0x5f, 0x0e, 0xe3, 0xc4, 0xec, 0x76, 0x44, 0xce, 0x0c,
+	0xce, 0x97, 0xbd, 0x37, 0x46, 0xd1, 0x7b, 0xe5, 0x45, 0xc8, 0xbf, 0xeb, 0xf2, 0xf9, 0xfb, 0xc5,
+	0x6c, 0xaa, 0x52, 0xe5, 0xdb, 0xcd, 0x62, 0xa6, 0x64, 0xd7, 0x50, 0xe4, 0xe0, 0x9f, 0x45, 0xa7,
+	0xbc, 0xa3, 0xf3, 0x56, 0x9d, 0x39, 0x99, 0x75, 0x8b, 0xff, 0xfe, 0x64, 0x11, 0x90, 0xd6, 0xba,
+	0xc5, 0x0b, 0xe3, 0xe5, 0x8e, 0x74, 0xbd, 0xf3, 0xf2, 0xd4, 0xc9, 0x0f, 0x01, 0xb2, 0x00, 0x17,
+	0x4a, 0xcd, 0x46, 0xc3, 0xed, 0xd3, 0xd1, 0xb9, 0x9d, 0x10, 0xe7, 0xf6, 0x04, 0xbe, 0x68, 0x1f,
+	0xd2, 0xc4, 0x80, 0x73, 0x45, 0x6a, 0xed, 0xb4, 0xd9, 0x9d, 0x1e, 0x00, 0xbb, 0x31, 0x37, 0xa2,
+	0x4f, 0xad, 0x0a, 0x17, 0xe8, 0x23, 0x5a, 0x35, 0x69, 0xd1, 0x64, 0xed, 0x2c, 0xc3, 0x03, 0xc8,
+	0x32, 0xd1, 0x0e, 0xeb, 0xa7, 0xfa, 0x14, 0xc0, 0xb4, 0x4b, 0x3b, 0xac, 0x6c, 0x6c, 0x31, 0x36,
+	0x3d, 0x32, 0x80, 0x1c, 0xa3, 0x5e, 0xbc, 0x35, 0xc6, 0xc8, 0x67, 0x30, 0x56, 0xda, 0xa6, 0x56,
+	0x85, 0x19, 0x0d, 0xca, 0xd9, 0xf4, 0x99, 0x01, 0x44, 0x07, 0x2f, 0x60, 0x81, 0x72, 0xa6, 0xdd,
+	0x81, 0xeb, 0xf2, 0xe9, 0xe2, 0xe4, 0x5b, 0x1b, 0xee, 0x09, 0x14, 0x7d, 0x3c, 0x15, 0x20, 0x15,
+	0xe7, 0x8e, 0x5f, 0x74, 0xba, 0xbd, 0x8b, 0x7d, 0x0b, 0xb1, 0x8b, 0x47, 0x0b, 0xdd, 0xcb, 0x5a,
+	0x15, 0x66, 0xfd, 0x89, 0x90, 0x6b, 0x72, 0xfb, 0xbe, 0x10, 0xda, 0xff, 0x8f, 0xa0, 0xf8, 0x55,
+	0x81, 0x2b, 0xe1, 0xb9, 0x10, 0xf9, 0x27, 0x40, 0x7a, 0x15, 0x3f, 0x8e, 0xa0, 0xd7, 0x65, 0x3b,
+	0xa5, 0x3b, 0x12, 0xee, 0x96, 0x09, 0xda, 0xb5, 0x3e, 0xb0, 0x31, 0xb4, 0xf4, 0xed, 0x04, 0x0c,
+	0x0b, 0x1e, 0x64, 0x1f, 0x46, 0x3c, 0x49, 0x4b, 0x52, 0x32, 0x68, 0xbd, 0xc2, 0x5e, 0x9d, 0x8b,
+	0xb5, 0xf3, 0x12, 0x6a, 0xda, 0x17, 0x7f, 0xfc, 0xfb, 0xf5, 0xa9, 0x19, 0xa2, 0xea, 0xa1, 0x77,
+	0x18, 0xf2, 0xa3, 0x02, 0x17, 0x7a, 0x14, 0x39, 0xc9, 0xc6, 0xa4, 0xe8, 0x15, 0xff, 0xea, 0xd2,
+	0x71, 0x5c, 0x10, 0x60, 0x46, 0x00, 0x4c, 0x93, 0x54, 0x38, 0x40, 0x7d, 0xaf, 0x3d, 0x9a, 0xf6,
+	0xc9, 0x63, 0x05, 0xce, 0xfa, 0x82, 0x9d, 0xa4, 0x43, 0x13, 0x76, 0xdd, 0x02, 0xd4, 0xf9, 0x3e,
+	0x2c, 0x11, 0x91, 0x2e, 0x10, 0xcd, 0x93, 0x39, 0x3d, 0xe2, 0x66, 0xe8, 0xe8, 0x7b, 0x78, 0x2c,
+	0xec, 0x93, 0xef, 0x14, 0x38, 0x17, 0x1c, 0xb4, 0x44, 0x0f, 0x4d, 0x26, 0xbf, 0x11, 0xa8, 0x37,
+	0xfb, 0x77, 0x40, 0x90, 0xcb, 0x02, 0xe4, 0x22, 0x59, 0xd0, 0xe3, 0x2e, 0x88, 0x01, 0xa0, 0xdf,
+	0x28, 0x70, 0x3e, 0x18, 0x2d, 0x67, 0x9a, 0x11, 0x58, 0xe5, 0xf7, 0x82, 0x08, 0xac, 0x21, 0x02,
+	0x5f, 0x9b, 0x17, 0x58, 0xaf, 0x91, 0xab, 0xb1, 0x58, 0xc9, 0x2f, 0x0a, 0x4c, 0x4a, 0x84, 0x1e,
+	0x59, 0x89, 0x2a, 0x50, 0xb8, 0xb0, 0x55, 0x6f, 0x1d, 0xdb, 0x0f, 0x31, 0xaf, 0x0a, 0xcc, 0xcb,
+	0x24, 0xab, 0xf7, 0x7b, 0x61, 0xd7, 0xf7, 0xc4, 0x74, 0xdd, 0x27, 0x4f, 0x15, 0x78, 0x55, 0x12,
+	0xda, 0x2d, 0xf6, 0x4a, 0x54, 0xed, 0x4e, 0x44, 0x23, 0x5a, 0x68, 0x6b, 0x59, 0x41, 0x63, 0x81,
+	0xcc, 0xf7, 0x4d, 0x83, 0xfc, 0xa4, 0xc0, 0x78, 0x67, 0xc8, 0x88, 0x51, 0x10, 0x26, 0x8a, 0x23,
+	0x46, 0x41, 0xa8, 0x24, 0xd6, 0x96, 0x04, 0xd8, 0x37, 0xc8, 0x8d, 0x3e, 0xc0, 0xea, 0x7b, 0xae,
+	0xce, 0xde, 0x27, 0xdf, 0x8b, 0xd9, 0x15, 0x0c, 0xe7, 0xd6, 0x39, 0xdb, 0x7f, 0xbd, 0xe2, 0x01,
+	0x87, 0x8a, 0x5c, 0x6d, 0x41, 0x00, 0xbe, 0x4e, 0xae, 0xf5, 0x01, 0x98, 0xfc, 0xa0, 0xc0, 0x78,
+	0xa7, 0x70, 0x24, 0x99, 0xd0, 0x9c, 0x52, 0x69, 0xaa, 0xea, 0x7d, 0xdb, 0x23, 0xc0, 0xb7, 0x04,
+	0x40, 0x9d, 0x2c, 0xca, 0x00, 0x76, 0x69, 0xd5, 0xc0, 0x9c, 0xf8, 0x53, 0x81, 0x4b, 0xa1, 0xe2,
+	0x80, 0xac, 0xf6, 0xdf, 0xda, 0x2e, 0x3d, 0xa2, 0xde, 0x3e, 0x89, 0x2b, 0x72, 0xc9, 0x09, 0x2e,
+	0xef, 0x90, 0x55, 0x19, 0x97, 0x0a, 0xe3, 0x46, 0x57, 0xc1, 0x1d, 0xa3, 0xd8, 0x32, 0xc4, 0x96,
+	0x6c, 0xef, 0xcc, 0x9f, 0x15, 0x98, 0xec, 0x3e, 0xe7, 0xdd, 0xcf, 0x65, 0x39, 0xaa, 0xf7, 0x21,
+	0x72, 0x46, 0x7d, 0xf3, 0x78, 0x4e, 0xfd, 0x1c, 0x77, 0xbd, 0x8a, 0x25, 0xbf, 0xfe, 0xec, 0x20,
+	0xa9, 0x3c, 0x3f, 0x48, 0x2a, 0xff, 0x1c, 0x24, 0x95, 0xc7, 0x87, 0xc9, 0xa1, 0xe7, 0x87, 0xc9,
+	0xa1, 0xbf, 0x0e, 0x93, 0x43, 0x0f, 0xf4, 0x80, 0x8c, 0x2c, 0x5a, 0xc5, 0xc5, 0xd2, 0x36, 0xad,
+	0x5a, 0xc1, 0xa8, 0x9f, 0xb7, 0xe3, 0x0a, 0x4d, 0x59, 0x1c, 0x11, 0xff, 0x53, 0xb8, 0xfc, 0x5f,
+	0x00, 0x00, 0x00, 0xff, 0xff, 0x40, 0x26, 0x77, 0xa3, 0xcd, 0x15, 0x00, 0x00,
 }
 
 // Reference imports to suppress errors if they are not otherwise used.
@@ -1180,6 +1275,8 @@ type QueryClient interface {
 	// ParamsByTimestamp queries the parameters of the module.
 	ParamsByTimestamp(ctx context.Context, in *QueryParamsByTimestampRequest, opts ...grpc.CallOption) (*QueryParamsByTimestampResponse, error)
 	// Queries a StreamRecord by index.
+	OutFlows(ctx context.Context, in *QueryOutFlowsRequest, opts ...grpc.CallOption) (*QueryOutFlowsResponse, error)
+	// Queries a StreamRecord by index.
 	StreamRecord(ctx context.Context, in *QueryGetStreamRecordRequest, opts ...grpc.CallOption) (*QueryGetStreamRecordResponse, error)
 	// Queries a list of StreamRecord items.
 	StreamRecordAll(ctx context.Context, in *QueryAllStreamRecordRequest, opts ...grpc.CallOption) (*QueryAllStreamRecordResponse, error)
@@ -1225,6 +1322,15 @@ func (c *queryClient) ParamsByTimestamp(ctx context.Context, in *QueryParamsByTi
 	return out, nil
 }
 
+func (c *queryClient) OutFlows(ctx context.Context, in *QueryOutFlowsRequest, opts ...grpc.CallOption) (*QueryOutFlowsResponse, error) {
+	out := new(QueryOutFlowsResponse)
+	err := c.cc.Invoke(ctx, "/greenfield.payment.Query/OutFlows", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
 func (c *queryClient) StreamRecord(ctx context.Context, in *QueryGetStreamRecordRequest, opts ...grpc.CallOption) (*QueryGetStreamRecordResponse, error) {
 	out := new(QueryGetStreamRecordResponse)
 	err := c.cc.Invoke(ctx, "/greenfield.payment.Query/StreamRecord", in, out, opts...)
@@ -1313,6 +1419,8 @@ type QueryServer interface {
 	// ParamsByTimestamp queries the parameters of the module.
 	ParamsByTimestamp(context.Context, *QueryParamsByTimestampRequest) (*QueryParamsByTimestampResponse, error)
 	// Queries a StreamRecord by index.
+	OutFlows(context.Context, *QueryOutFlowsRequest) (*QueryOutFlowsResponse, error)
+	// Queries a StreamRecord by index.
 	StreamRecord(context.Context, *QueryGetStreamRecordRequest) (*QueryGetStreamRecordResponse, error)
 	// Queries a list of StreamRecord items.
 	StreamRecordAll(context.Context, *QueryAllStreamRecordRequest) (*QueryAllStreamRecordResponse, error)
@@ -1342,6 +1450,9 @@ func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsReq
 func (*UnimplementedQueryServer) ParamsByTimestamp(ctx context.Context, req *QueryParamsByTimestampRequest) (*QueryParamsByTimestampResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method ParamsByTimestamp not implemented")
 }
+func (*UnimplementedQueryServer) OutFlows(ctx context.Context, req *QueryOutFlowsRequest) (*QueryOutFlowsResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method OutFlows not implemented")
+}
 func (*UnimplementedQueryServer) StreamRecord(ctx context.Context, req *QueryGetStreamRecordRequest) (*QueryGetStreamRecordResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method StreamRecord not implemented")
 }
@@ -1410,6 +1521,24 @@ func _Query_ParamsByTimestamp_Handler(srv interface{}, ctx context.Context, dec
 	return interceptor(ctx, in, info, handler)
 }
 
+func _Query_OutFlows_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(QueryOutFlowsRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(QueryServer).OutFlows(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/greenfield.payment.Query/OutFlows",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(QueryServer).OutFlows(ctx, req.(*QueryOutFlowsRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
 func _Query_StreamRecord_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
 	in := new(QueryGetStreamRecordRequest)
 	if err := dec(in); err != nil {
@@ -1584,6 +1713,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{
 			MethodName: "ParamsByTimestamp",
 			Handler:    _Query_ParamsByTimestamp_Handler,
 		},
+		{
+			MethodName: "OutFlows",
+			Handler:    _Query_OutFlows_Handler,
+		},
 		{
 			MethodName: "StreamRecord",
 			Handler:    _Query_StreamRecord_Handler,
@@ -1742,6 +1875,73 @@ func (m *QueryParamsByTimestampResponse) MarshalToSizedBuffer(dAtA []byte) (int,
 	return len(dAtA) - i, nil
 }
 
+func (m *QueryOutFlowsRequest) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *QueryOutFlowsRequest) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryOutFlowsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.Account) > 0 {
+		i -= len(m.Account)
+		copy(dAtA[i:], m.Account)
+		i = encodeVarintQuery(dAtA, i, uint64(len(m.Account)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *QueryOutFlowsResponse) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *QueryOutFlowsResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryOutFlowsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.OutFlows) > 0 {
+		for iNdEx := len(m.OutFlows) - 1; iNdEx >= 0; iNdEx-- {
+			{
+				size, err := m.OutFlows[iNdEx].MarshalToSizedBuffer(dAtA[:i])
+				if err != nil {
+					return 0, err
+				}
+				i -= size
+				i = encodeVarintQuery(dAtA, i, uint64(size))
+			}
+			i--
+			dAtA[i] = 0xa
+		}
+	}
+	return len(dAtA) - i, nil
+}
+
 func (m *QueryGetStreamRecordRequest) Marshal() (dAtA []byte, err error) {
 	size := m.Size()
 	dAtA = make([]byte, size)
@@ -2501,6 +2701,34 @@ func (m *QueryParamsByTimestampResponse) Size() (n int) {
 	return n
 }
 
+func (m *QueryOutFlowsRequest) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Account)
+	if l > 0 {
+		n += 1 + l + sovQuery(uint64(l))
+	}
+	return n
+}
+
+func (m *QueryOutFlowsResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if len(m.OutFlows) > 0 {
+		for _, e := range m.OutFlows {
+			l = e.Size()
+			n += 1 + l + sovQuery(uint64(l))
+		}
+	}
+	return n
+}
+
 func (m *QueryGetStreamRecordRequest) Size() (n int) {
 	if m == nil {
 		return 0
@@ -3057,6 +3285,172 @@ func (m *QueryParamsByTimestampResponse) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
+func (m *QueryOutFlowsRequest) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowQuery
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: QueryOutFlowsRequest: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: QueryOutFlowsRequest: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Account", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthQuery
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Account = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipQuery(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *QueryOutFlowsResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowQuery
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: QueryOutFlowsResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: QueryOutFlowsResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field OutFlows", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthQuery
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.OutFlows = append(m.OutFlows, OutFlow{})
+			if err := m.OutFlows[len(m.OutFlows)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipQuery(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
 func (m *QueryGetStreamRecordRequest) Unmarshal(dAtA []byte) error {
 	l := len(dAtA)
 	iNdEx := 0
diff --git a/x/payment/types/query.pb.gw.go b/x/payment/types/query.pb.gw.go
index 9178ec63e..3b840a38b 100644
--- a/x/payment/types/query.pb.gw.go
+++ b/x/payment/types/query.pb.gw.go
@@ -105,6 +105,60 @@ func local_request_Query_ParamsByTimestamp_0(ctx context.Context, marshaler runt
 
 }
 
+func request_Query_OutFlows_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryOutFlowsRequest
+	var metadata runtime.ServerMetadata
+
+	var (
+		val string
+		ok  bool
+		err error
+		_   = err
+	)
+
+	val, ok = pathParams["account"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "account")
+	}
+
+	protoReq.Account, err = runtime.String(val)
+
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "account", err)
+	}
+
+	msg, err := client.OutFlows(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_Query_OutFlows_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryOutFlowsRequest
+	var metadata runtime.ServerMetadata
+
+	var (
+		val string
+		ok  bool
+		err error
+		_   = err
+	)
+
+	val, ok = pathParams["account"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "account")
+	}
+
+	protoReq.Account, err = runtime.String(val)
+
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "account", err)
+	}
+
+	msg, err := server.OutFlows(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
 func request_Query_StreamRecord_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
 	var protoReq QueryGetStreamRecordRequest
 	var metadata runtime.ServerMetadata
@@ -571,6 +625,29 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv
 
 	})
 
+	mux.Handle("GET", pattern_Query_OutFlows_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		var stream runtime.ServerTransportStream
+		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_Query_OutFlows_0(rctx, inboundMarshaler, server, req, pathParams)
+		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_Query_OutFlows_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
 	mux.Handle("GET", pattern_Query_StreamRecord_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
 		ctx, cancel := context.WithCancel(req.Context())
 		defer cancel()
@@ -859,6 +936,26 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie
 
 	})
 
+	mux.Handle("GET", pattern_Query_OutFlows_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_Query_OutFlows_0(rctx, inboundMarshaler, client, req, pathParams)
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_Query_OutFlows_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
 	mux.Handle("GET", pattern_Query_StreamRecord_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
 		ctx, cancel := context.WithCancel(req.Context())
 		defer cancel()
@@ -1047,6 +1144,8 @@ var (
 
 	pattern_Query_ParamsByTimestamp_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"greenfield", "payment", "params", "timestamp"}, "", runtime.AssumeColonVerbOpt(false)))
 
+	pattern_Query_OutFlows_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"greenfield", "payment", "out_flows", "account"}, "", runtime.AssumeColonVerbOpt(false)))
+
 	pattern_Query_StreamRecord_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"greenfield", "payment", "stream_record", "account"}, "", runtime.AssumeColonVerbOpt(false)))
 
 	pattern_Query_StreamRecordAll_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"greenfield", "payment", "stream_record"}, "", runtime.AssumeColonVerbOpt(false)))
@@ -1071,6 +1170,8 @@ var (
 
 	forward_Query_ParamsByTimestamp_0 = runtime.ForwardResponseMessage
 
+	forward_Query_OutFlows_0 = runtime.ForwardResponseMessage
+
 	forward_Query_StreamRecord_0 = runtime.ForwardResponseMessage
 
 	forward_Query_StreamRecordAll_0 = runtime.ForwardResponseMessage
diff --git a/x/payment/types/stream_record.pb.go b/x/payment/types/stream_record.pb.go
index 3b45cf555..cfb8b344d 100644
--- a/x/payment/types/stream_record.pb.go
+++ b/x/payment/types/stream_record.pb.go
@@ -25,6 +25,36 @@ var _ = math.Inf
 // proto package needs to be updated.
 const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
 
+// StreamAccountStatus defines the status of a stream account
+type StreamAccountStatus int32
+
+const (
+	// STREAM_ACCOUNT_STATUS_ACTIVE defines the active status of a stream account.
+	STREAM_ACCOUNT_STATUS_ACTIVE StreamAccountStatus = 0
+	// STREAM_ACCOUNT_STATUS_FROZEN defines the frozen status of a stream account.
+	// A frozen stream account cannot be used as payment address for buckets.
+	// It can be unfrozen by depositing more BNB to the stream account.
+	STREAM_ACCOUNT_STATUS_FROZEN StreamAccountStatus = 1
+)
+
+var StreamAccountStatus_name = map[int32]string{
+	0: "STREAM_ACCOUNT_STATUS_ACTIVE",
+	1: "STREAM_ACCOUNT_STATUS_FROZEN",
+}
+
+var StreamAccountStatus_value = map[string]int32{
+	"STREAM_ACCOUNT_STATUS_ACTIVE": 0,
+	"STREAM_ACCOUNT_STATUS_FROZEN": 1,
+}
+
+func (x StreamAccountStatus) String() string {
+	return proto.EnumName(StreamAccountStatus_name, int32(x))
+}
+
+func (StreamAccountStatus) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptor_5016dfb5c390a707, []int{0}
+}
+
 // Stream Payment Record of a stream account
 type StreamRecord struct {
 	// account address
@@ -45,8 +75,10 @@ type StreamRecord struct {
 	Status StreamAccountStatus `protobuf:"varint,7,opt,name=status,proto3,enum=greenfield.payment.StreamAccountStatus" json:"status,omitempty"`
 	// the unix timestamp when the stream account will be settled
 	SettleTimestamp int64 `protobuf:"varint,8,opt,name=settle_timestamp,json=settleTimestamp,proto3" json:"settle_timestamp,omitempty"`
-	// the accumulated outflow rates of the stream account
-	OutFlows []OutFlow `protobuf:"bytes,9,rep,name=out_flows,json=outFlows,proto3" json:"out_flows"`
+	// the count of its out flows
+	OutFlowCount uint64 `protobuf:"varint,9,opt,name=out_flow_count,json=outFlowCount,proto3" json:"out_flow_count,omitempty"`
+	// the frozen netflow rate, which is used when resuming stream account
+	FrozenNetflowRate github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,10,opt,name=frozen_netflow_rate,json=frozenNetflowRate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"frozen_netflow_rate"`
 }
 
 func (m *StreamRecord) Reset()         { *m = StreamRecord{} }
@@ -110,14 +142,15 @@ func (m *StreamRecord) GetSettleTimestamp() int64 {
 	return 0
 }
 
-func (m *StreamRecord) GetOutFlows() []OutFlow {
+func (m *StreamRecord) GetOutFlowCount() uint64 {
 	if m != nil {
-		return m.OutFlows
+		return m.OutFlowCount
 	}
-	return nil
+	return 0
 }
 
 func init() {
+	proto.RegisterEnum("greenfield.payment.StreamAccountStatus", StreamAccountStatus_name, StreamAccountStatus_value)
 	proto.RegisterType((*StreamRecord)(nil), "greenfield.payment.StreamRecord")
 }
 
@@ -126,36 +159,40 @@ func init() {
 }
 
 var fileDescriptor_5016dfb5c390a707 = []byte{
-	// 457 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x92, 0xc1, 0x6e, 0xd3, 0x40,
-	0x10, 0x86, 0x63, 0xd2, 0xa6, 0xcd, 0x36, 0x0d, 0xc8, 0xea, 0xc1, 0x14, 0xe1, 0x46, 0x48, 0x94,
-	0x70, 0x88, 0x2d, 0x85, 0x2b, 0x02, 0x35, 0x07, 0xa4, 0x9c, 0x90, 0x1c, 0x4e, 0x5c, 0xac, 0xf5,
-	0x7a, 0xe2, 0x5a, 0xb5, 0x77, 0xa3, 0xdd, 0xb1, 0x4a, 0xdf, 0x82, 0x57, 0xe0, 0x1d, 0xfa, 0x10,
-	0x3d, 0x56, 0x3d, 0x21, 0x0e, 0x15, 0x4a, 0x5e, 0x04, 0x79, 0x77, 0x9d, 0x46, 0x22, 0xc7, 0x9c,
-	0xec, 0x1d, 0xff, 0xfe, 0xbf, 0x9d, 0x99, 0x9f, 0x9c, 0x67, 0x12, 0x80, 0xcf, 0x73, 0x28, 0xd2,
-	0x70, 0x41, 0x6f, 0x4a, 0xe0, 0x18, 0x2a, 0x94, 0x40, 0xcb, 0x58, 0x02, 0x13, 0x32, 0x0d, 0x16,
-	0x52, 0xa0, 0x70, 0xdd, 0x27, 0x5d, 0x60, 0x75, 0xa7, 0x2f, 0x99, 0x50, 0xa5, 0x50, 0xb1, 0x56,
-	0x84, 0xe6, 0x60, 0xe4, 0xa7, 0x27, 0x99, 0xc8, 0x84, 0xa9, 0xd7, 0x6f, 0xb6, 0xfa, 0x7a, 0x0b,
-	0x2c, 0xa1, 0x0a, 0xcc, 0xe7, 0x37, 0xbf, 0xf6, 0x49, 0x6f, 0xa6, 0xd9, 0x91, 0x46, 0xbb, 0x63,
-	0x72, 0x40, 0x19, 0x13, 0x15, 0x47, 0xcf, 0x19, 0x38, 0xc3, 0xee, 0xc4, 0x7b, 0xb8, 0x1d, 0x9d,
-	0x58, 0xd0, 0x45, 0x9a, 0x4a, 0x50, 0x6a, 0x86, 0x32, 0xe7, 0x59, 0xd4, 0x08, 0xdd, 0xb7, 0xa4,
-	0xcf, 0x64, 0x95, 0xc6, 0x98, 0x97, 0xa0, 0x90, 0x96, 0x0b, 0xef, 0xd9, 0xc0, 0x19, 0xb6, 0xa3,
-	0xe3, 0xba, 0xfa, 0xad, 0x29, 0xba, 0x31, 0xe9, 0x71, 0xc0, 0x79, 0x21, 0xae, 0x63, 0x49, 0x11,
-	0xbc, 0xb6, 0xf6, 0xff, 0x78, 0xf7, 0x78, 0xd6, 0xfa, 0xf3, 0x78, 0x76, 0x9e, 0xe5, 0x78, 0x59,
-	0x25, 0x01, 0x13, 0xa5, 0xed, 0xcb, 0x3e, 0x46, 0x2a, 0xbd, 0x0a, 0xf1, 0x66, 0x01, 0x2a, 0x98,
-	0x72, 0x7c, 0xb8, 0x1d, 0x11, 0x7b, 0x9b, 0x29, 0xc7, 0xe8, 0xc8, 0x3a, 0x46, 0x14, 0xc1, 0x65,
-	0xa4, 0xaf, 0x90, 0x62, 0xce, 0xe2, 0x84, 0x16, 0x94, 0x33, 0xf0, 0xf6, 0x76, 0x80, 0x38, 0x36,
-	0x9e, 0x13, 0x63, 0x59, 0x43, 0x92, 0x6a, 0x3e, 0x07, 0xb9, 0x86, 0xec, 0xef, 0x02, 0x62, 0x3c,
-	0x1b, 0x48, 0x4c, 0x7a, 0x85, 0x60, 0x57, 0x6b, 0x44, 0x67, 0x17, 0xa3, 0xaa, 0x1d, 0x1b, 0xc0,
-	0x67, 0xd2, 0xa9, 0xdb, 0xaa, 0x94, 0x77, 0x30, 0x70, 0x86, 0xfd, 0xf1, 0xbb, 0xe0, 0xff, 0xb0,
-	0x05, 0x26, 0x18, 0x17, 0x66, 0xcb, 0x33, 0x2d, 0x8f, 0xec, 0x6f, 0xee, 0x7b, 0xf2, 0x42, 0x01,
-	0x62, 0x01, 0x1b, 0x5b, 0x3f, 0xd4, 0x5b, 0x7f, 0x6e, 0xea, 0x4f, 0x7b, 0xff, 0x44, 0xba, 0xa2,
-	0xc2, 0xb8, 0x5e, 0x93, 0xf2, 0xba, 0x83, 0xf6, 0xf0, 0x68, 0xfc, 0x6a, 0x1b, 0xee, 0x6b, 0x85,
-	0x5f, 0x0a, 0x71, 0x3d, 0xd9, 0xab, 0xdb, 0x8c, 0x0e, 0x85, 0x39, 0xaa, 0xc9, 0xf4, 0x6e, 0xe9,
-	0x3b, 0xf7, 0x4b, 0xdf, 0xf9, 0xbb, 0xf4, 0x9d, 0x9f, 0x2b, 0xbf, 0x75, 0xbf, 0xf2, 0x5b, 0xbf,
-	0x57, 0x7e, 0xeb, 0x7b, 0xb8, 0x31, 0x88, 0x84, 0x27, 0x23, 0x76, 0x49, 0x73, 0x1e, 0x6e, 0x24,
-	0xfe, 0xc7, 0x3a, 0xf3, 0x7a, 0x2a, 0x49, 0x47, 0xa7, 0xfe, 0xc3, 0xbf, 0x00, 0x00, 0x00, 0xff,
-	0xff, 0x30, 0xf4, 0x1a, 0xe9, 0x83, 0x03, 0x00, 0x00,
+	// 519 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x93, 0x4f, 0x6e, 0xd3, 0x40,
+	0x14, 0xc6, 0x6d, 0xda, 0xa6, 0x74, 0x48, 0x43, 0x70, 0xbb, 0x30, 0x11, 0x72, 0x23, 0x04, 0x25,
+	0x20, 0xc5, 0x96, 0xca, 0x16, 0x09, 0x39, 0x51, 0x2a, 0x65, 0x41, 0x2a, 0xd9, 0x2e, 0x8b, 0x6e,
+	0x46, 0xf6, 0x78, 0x92, 0x5a, 0xb5, 0x67, 0xa2, 0x99, 0x67, 0x95, 0x72, 0x02, 0x96, 0xdc, 0x81,
+	0x2b, 0xf4, 0x10, 0x5d, 0x56, 0x5d, 0x21, 0x16, 0x15, 0x4a, 0xce, 0xc0, 0x1e, 0xc5, 0xe3, 0xfc,
+	0x41, 0xc0, 0x2e, 0x2b, 0xdb, 0x4f, 0x9f, 0x7f, 0xdf, 0xbc, 0x79, 0xdf, 0x43, 0x87, 0x23, 0x41,
+	0x29, 0x1b, 0x26, 0x34, 0x8d, 0x9d, 0x71, 0x78, 0x95, 0x51, 0x06, 0x8e, 0x04, 0x41, 0xc3, 0x0c,
+	0x0b, 0x4a, 0xb8, 0x88, 0xed, 0xb1, 0xe0, 0xc0, 0x0d, 0x63, 0xa9, 0xb3, 0x4b, 0x5d, 0xe3, 0x29,
+	0xe1, 0x32, 0xe3, 0x12, 0x17, 0x0a, 0x47, 0x7d, 0x28, 0x79, 0x63, 0x7f, 0xc4, 0x47, 0x5c, 0xd5,
+	0x67, 0x6f, 0xaa, 0xfa, 0xfc, 0xd7, 0x16, 0xaa, 0xfa, 0x05, 0xdc, 0x2b, 0xd8, 0xc6, 0x11, 0xda,
+	0x0e, 0x09, 0xe1, 0x39, 0x03, 0x53, 0x6f, 0xea, 0xad, 0x9d, 0x8e, 0x79, 0x77, 0xdd, 0xde, 0x2f,
+	0x49, 0x6e, 0x1c, 0x0b, 0x2a, 0xa5, 0x0f, 0x22, 0x61, 0x23, 0x6f, 0x2e, 0x34, 0x5e, 0xa2, 0x1a,
+	0x11, 0x79, 0x8c, 0x21, 0xc9, 0xa8, 0x84, 0x30, 0x1b, 0x9b, 0x0f, 0x9a, 0x7a, 0x6b, 0xc3, 0xdb,
+	0x9d, 0x55, 0x83, 0x79, 0xd1, 0xc0, 0xa8, 0xca, 0x28, 0x0c, 0x53, 0x7e, 0x89, 0x45, 0x08, 0xd4,
+	0xdc, 0x28, 0xf8, 0xef, 0x6e, 0xee, 0x0f, 0xb4, 0x1f, 0xf7, 0x07, 0x87, 0xa3, 0x04, 0xce, 0xf3,
+	0xc8, 0x26, 0x3c, 0x2b, 0x0f, 0x5e, 0x3e, 0xda, 0x32, 0xbe, 0x70, 0xe0, 0x6a, 0x4c, 0xa5, 0xdd,
+	0x67, 0x70, 0x77, 0xdd, 0x46, 0xe5, 0x69, 0xfa, 0x0c, 0xbc, 0x47, 0x25, 0xd1, 0x0b, 0x81, 0x1a,
+	0x04, 0xd5, 0x24, 0x84, 0x90, 0x10, 0x1c, 0x85, 0x69, 0xc8, 0x08, 0x35, 0x37, 0xd7, 0x60, 0xb1,
+	0xab, 0x98, 0x1d, 0x85, 0x9c, 0x99, 0x44, 0xf9, 0x70, 0x48, 0xc5, 0xc2, 0x64, 0x6b, 0x1d, 0x26,
+	0x8a, 0x39, 0x37, 0xc1, 0xa8, 0x9a, 0x72, 0x72, 0xb1, 0xb0, 0xa8, 0xac, 0xe3, 0xaa, 0x66, 0xc4,
+	0xb9, 0xc1, 0x7b, 0x54, 0x99, 0xb5, 0x95, 0x4b, 0x73, 0xbb, 0xa9, 0xb7, 0x6a, 0x47, 0xaf, 0xec,
+	0xbf, 0xd3, 0x64, 0xab, 0x60, 0xb8, 0x6a, 0xca, 0x7e, 0x21, 0xf7, 0xca, 0xdf, 0x8c, 0xd7, 0xa8,
+	0x2e, 0x29, 0x40, 0x4a, 0x57, 0xa6, 0xfe, 0xb0, 0x98, 0xfa, 0x63, 0x55, 0x5f, 0xce, 0xfd, 0x05,
+	0xaa, 0xf1, 0x1c, 0x70, 0x31, 0x78, 0x95, 0xac, 0x9d, 0xa6, 0xde, 0xda, 0xf4, 0xaa, 0x3c, 0x87,
+	0xe3, 0x94, 0x5f, 0x76, 0x8b, 0x10, 0xa5, 0x68, 0x6f, 0x28, 0xf8, 0x67, 0xca, 0xf0, 0x1f, 0x21,
+	0x41, 0x6b, 0xe8, 0xfc, 0x89, 0x02, 0x0f, 0x96, 0x51, 0x79, 0x83, 0xd1, 0xde, 0x3f, 0xba, 0x33,
+	0x9a, 0xe8, 0x99, 0x1f, 0x78, 0x3d, 0xf7, 0x03, 0x76, 0xbb, 0xdd, 0x93, 0xd3, 0x41, 0x80, 0xfd,
+	0xc0, 0x0d, 0x4e, 0x7d, 0xec, 0x76, 0x83, 0xfe, 0xc7, 0x5e, 0x5d, 0xfb, 0xbf, 0xe2, 0xd8, 0x3b,
+	0x39, 0xeb, 0x0d, 0xea, 0x7a, 0x63, 0xf3, 0xcb, 0x37, 0x4b, 0xeb, 0xf4, 0x6f, 0x26, 0x96, 0x7e,
+	0x3b, 0xb1, 0xf4, 0x9f, 0x13, 0x4b, 0xff, 0x3a, 0xb5, 0xb4, 0xdb, 0xa9, 0xa5, 0x7d, 0x9f, 0x5a,
+	0xda, 0x99, 0xb3, 0xd2, 0x43, 0xc4, 0xa2, 0x36, 0x39, 0x0f, 0x13, 0xe6, 0xac, 0x2c, 0xfd, 0xa7,
+	0xc5, 0xda, 0x17, 0x0d, 0x45, 0x95, 0x62, 0x55, 0xdf, 0xfe, 0x0e, 0x00, 0x00, 0xff, 0xff, 0x45,
+	0x76, 0x89, 0x00, 0x19, 0x04, 0x00, 0x00,
 }
 
 func (m *StreamRecord) Marshal() (dAtA []byte, err error) {
@@ -178,19 +215,20 @@ func (m *StreamRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	_ = i
 	var l int
 	_ = l
-	if len(m.OutFlows) > 0 {
-		for iNdEx := len(m.OutFlows) - 1; iNdEx >= 0; iNdEx-- {
-			{
-				size, err := m.OutFlows[iNdEx].MarshalToSizedBuffer(dAtA[:i])
-				if err != nil {
-					return 0, err
-				}
-				i -= size
-				i = encodeVarintStreamRecord(dAtA, i, uint64(size))
-			}
-			i--
-			dAtA[i] = 0x4a
+	{
+		size := m.FrozenNetflowRate.Size()
+		i -= size
+		if _, err := m.FrozenNetflowRate.MarshalTo(dAtA[i:]); err != nil {
+			return 0, err
 		}
+		i = encodeVarintStreamRecord(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0x52
+	if m.OutFlowCount != 0 {
+		i = encodeVarintStreamRecord(dAtA, i, uint64(m.OutFlowCount))
+		i--
+		dAtA[i] = 0x48
 	}
 	if m.SettleTimestamp != 0 {
 		i = encodeVarintStreamRecord(dAtA, i, uint64(m.SettleTimestamp))
@@ -295,12 +333,11 @@ func (m *StreamRecord) Size() (n int) {
 	if m.SettleTimestamp != 0 {
 		n += 1 + sovStreamRecord(uint64(m.SettleTimestamp))
 	}
-	if len(m.OutFlows) > 0 {
-		for _, e := range m.OutFlows {
-			l = e.Size()
-			n += 1 + l + sovStreamRecord(uint64(l))
-		}
+	if m.OutFlowCount != 0 {
+		n += 1 + sovStreamRecord(uint64(m.OutFlowCount))
 	}
+	l = m.FrozenNetflowRate.Size()
+	n += 1 + l + sovStreamRecord(uint64(l))
 	return n
 }
 
@@ -565,10 +602,29 @@ func (m *StreamRecord) Unmarshal(dAtA []byte) error {
 				}
 			}
 		case 9:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field OutFlowCount", wireType)
+			}
+			m.OutFlowCount = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowStreamRecord
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.OutFlowCount |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 10:
 			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field OutFlows", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field FrozenNetflowRate", wireType)
 			}
-			var msglen int
+			var stringLen uint64
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowStreamRecord
@@ -578,23 +634,23 @@ func (m *StreamRecord) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				msglen |= int(b&0x7F) << shift
+				stringLen |= uint64(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
-			if msglen < 0 {
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
 				return ErrInvalidLengthStreamRecord
 			}
-			postIndex := iNdEx + msglen
+			postIndex := iNdEx + intStringLen
 			if postIndex < 0 {
 				return ErrInvalidLengthStreamRecord
 			}
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			m.OutFlows = append(m.OutFlows, OutFlow{})
-			if err := m.OutFlows[len(m.OutFlows)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+			if err := m.FrozenNetflowRate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
 				return err
 			}
 			iNdEx = postIndex
diff --git a/x/payment/types/types.go b/x/payment/types/types.go
index a09933d85..251b1ba17 100644
--- a/x/payment/types/types.go
+++ b/x/payment/types/types.go
@@ -12,5 +12,5 @@ var (
 )
 
 const (
-	ForceUpdateFrozenStreamRecordKey = "force_update_frozen_stream_record"
+	ForceUpdateStreamRecordKey = "force_update_stream_record"
 )
diff --git a/x/permission/keeper/keeper.go b/x/permission/keeper/keeper.go
index b1f069ab9..d94e635ff 100644
--- a/x/permission/keeper/keeper.go
+++ b/x/permission/keeper/keeper.go
@@ -23,8 +23,8 @@ type (
 		accountKeeper types.AccountKeeper
 
 		// policy sequence
-		policySeq      sequence.U256
-		groupMemberSeq sequence.U256
+		policySeq      sequence.Sequence[math.Uint]
+		groupMemberSeq sequence.Sequence[math.Uint]
 
 		authority string
 	}
@@ -45,8 +45,8 @@ func NewKeeper(
 		accountKeeper: accountKeeper,
 		authority:     authority,
 	}
-	k.policySeq = sequence.NewSequence256(types.PolicySequencePrefix)
-	k.groupMemberSeq = sequence.NewSequence256(types.GroupMemberSequencePrefix)
+	k.policySeq = sequence.NewSequence[math.Uint](types.PolicySequencePrefix)
+	k.groupMemberSeq = sequence.NewSequence[math.Uint](types.GroupMemberSequencePrefix)
 	return k
 }
 
@@ -82,7 +82,7 @@ func (k Keeper) RemoveGroupMember(ctx sdk.Context, groupID math.Uint, member sdk
 		return storagetypes.ErrNoSuchGroupMember
 	}
 	store.Delete(memberKey)
-	store.Delete(types.GetGroupMemberByIDKey(sequence.DecodeSequence(bz)))
+	store.Delete(types.GetGroupMemberByIDKey(k.groupMemberSeq.DecodeSequence(bz)))
 	return nil
 }
 
@@ -94,7 +94,7 @@ func (k Keeper) GetGroupMember(ctx sdk.Context, groupID math.Uint, member sdk.Ac
 		return nil, false
 	}
 
-	return k.GetGroupMemberByID(ctx, sequence.DecodeSequence(bz))
+	return k.GetGroupMemberByID(ctx, k.groupMemberSeq.DecodeSequence(bz))
 }
 
 func (k Keeper) GetGroupMemberByID(ctx sdk.Context, groupMemberID math.Uint) (*types.GroupMember, bool) {
@@ -125,12 +125,12 @@ func (k Keeper) PutPolicy(ctx sdk.Context, policy *types.Policy) (math.Uint, err
 			policy.Principal.MustGetAccountAddress())
 		bz := store.Get(policyKey)
 		if bz != nil {
-			id := sequence.DecodeSequence(bz)
+			id := k.policySeq.DecodeSequence(bz)
 			// override write
 			newPolicy = k.updatePolicy(ctx, k.MustGetPolicyByID(ctx, id), policy)
 		} else {
 			policy.Id = k.policySeq.NextVal(store)
-			store.Set(policyKey, sequence.EncodeSequence(policy.Id))
+			store.Set(policyKey, k.policySeq.EncodeSequence(policy.Id))
 			bz := k.cdc.MustMarshal(policy)
 			store.Set(types.GetPolicyByIDKey(policy.Id), bz)
 			newPolicy = policy
@@ -221,7 +221,7 @@ func (k Keeper) GetPolicyForAccount(ctx sdk.Context, resourceID math.Uint,
 		return policy, false
 	}
 
-	return k.GetPolicyByID(ctx, sequence.DecodeSequence(bz))
+	return k.GetPolicyByID(ctx, k.policySeq.DecodeSequence(bz))
 }
 
 func (k Keeper) GetPolicyForGroup(ctx sdk.Context, resourceID math.Uint,
@@ -318,7 +318,7 @@ func (k Keeper) DeletePolicy(ctx sdk.Context, principal *types.Principal, resour
 		accAddr := sdk.MustAccAddressFromHex(principal.Value)
 		policyKey := types.GetPolicyForAccountKey(resourceID, resourceType, accAddr)
 		bz := store.Get(policyKey)
-		policyID = sequence.DecodeSequence(bz)
+		policyID = k.policySeq.DecodeSequence(bz)
 		if bz != nil {
 			store.Delete(policyKey)
 			store.Delete(types.GetPolicyByIDKey(policyID))
@@ -372,7 +372,7 @@ func (k Keeper) ForceDeleteAccountPolicyForResource(ctx sdk.Context, maxDelete,
 
 	for ; iterator.Valid(); iterator.Next() {
 		// delete mapping policyId -> policy
-		policyID := sequence.DecodeSequence(iterator.Value())
+		policyID := k.policySeq.DecodeSequence(iterator.Value())
 		store.Delete(types.GetPolicyByIDKey(policyID))
 		// delete mapping policyKey -> policyId
 		resourceAccountsPolicyStore.Delete(iterator.Key())
@@ -427,7 +427,7 @@ func (k Keeper) ForceDeleteGroupMembers(ctx sdk.Context, maxDelete, deletedTotal
 	iter := groupMembersPrefixStore.Iterator(nil, nil)
 	defer iter.Close()
 	for ; iter.Valid(); iter.Next() {
-		memberID := sequence.DecodeSequence(iter.Value())
+		memberID := k.groupMemberSeq.DecodeSequence(iter.Value())
 		// delete GroupMemberByIDPrefix_id -> groupMember
 		store.Delete(types.GetGroupMemberByIDKey(memberID))
 		// delete GroupMemberPrefix_groupId_memberAddr -> memberSequence(id)
diff --git a/x/permission/types/common.go b/x/permission/types/common.go
index 0d578e256..270f16459 100644
--- a/x/permission/types/common.go
+++ b/x/permission/types/common.go
@@ -1,10 +1,10 @@
 package types
 
 import (
-	"github.com/bnb-chain/greenfield/types"
-
 	sdkmath "cosmossdk.io/math"
 	sdk "github.com/cosmos/cosmos-sdk/types"
+
+	"github.com/bnb-chain/greenfield/types"
 )
 
 func NewPrincipalWithAccount(addr sdk.AccAddress) *Principal {
diff --git a/x/permission/types/expected_keepers_mocks.go b/x/permission/types/expected_keepers_mocks.go
index 3b2f03a4f..9852ab76f 100644
--- a/x/permission/types/expected_keepers_mocks.go
+++ b/x/permission/types/expected_keepers_mocks.go
@@ -12,76 +12,76 @@ import (
 	gomock "github.com/golang/mock/gomock"
 )
 
-// MockAccountKeeper is a mock of AccountKeeper interface
+// MockAccountKeeper is a mock of AccountKeeper interface.
 type MockAccountKeeper struct {
 	ctrl     *gomock.Controller
 	recorder *MockAccountKeeperMockRecorder
 }
 
-// MockAccountKeeperMockRecorder is the mock recorder for MockAccountKeeper
+// MockAccountKeeperMockRecorder is the mock recorder for MockAccountKeeper.
 type MockAccountKeeperMockRecorder struct {
 	mock *MockAccountKeeper
 }
 
-// NewMockAccountKeeper creates a new mock instance
+// NewMockAccountKeeper creates a new mock instance.
 func NewMockAccountKeeper(ctrl *gomock.Controller) *MockAccountKeeper {
 	mock := &MockAccountKeeper{ctrl: ctrl}
 	mock.recorder = &MockAccountKeeperMockRecorder{mock}
 	return mock
 }
 
-// EXPECT returns an object that allows the caller to indicate expected use
+// EXPECT returns an object that allows the caller to indicate expected use.
 func (m *MockAccountKeeper) EXPECT() *MockAccountKeeperMockRecorder {
 	return m.recorder
 }
 
-// GetAccount mocks base method
-func (m *MockAccountKeeper) GetAccount(arg0 types.Context, arg1 types.AccAddress) types0.AccountI {
+// GetAccount mocks base method.
+func (m *MockAccountKeeper) GetAccount(ctx types.Context, addr types.AccAddress) types0.AccountI {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetAccount", arg0, arg1)
+	ret := m.ctrl.Call(m, "GetAccount", ctx, addr)
 	ret0, _ := ret[0].(types0.AccountI)
 	return ret0
 }
 
-// GetAccount indicates an expected call of GetAccount
-func (mr *MockAccountKeeperMockRecorder) GetAccount(arg0, arg1 interface{}) *gomock.Call {
+// GetAccount indicates an expected call of GetAccount.
+func (mr *MockAccountKeeperMockRecorder) GetAccount(ctx, addr interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetAccount), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetAccount), ctx, addr)
 }
 
-// MockBankKeeper is a mock of BankKeeper interface
+// MockBankKeeper is a mock of BankKeeper interface.
 type MockBankKeeper struct {
 	ctrl     *gomock.Controller
 	recorder *MockBankKeeperMockRecorder
 }
 
-// MockBankKeeperMockRecorder is the mock recorder for MockBankKeeper
+// MockBankKeeperMockRecorder is the mock recorder for MockBankKeeper.
 type MockBankKeeperMockRecorder struct {
 	mock *MockBankKeeper
 }
 
-// NewMockBankKeeper creates a new mock instance
+// NewMockBankKeeper creates a new mock instance.
 func NewMockBankKeeper(ctrl *gomock.Controller) *MockBankKeeper {
 	mock := &MockBankKeeper{ctrl: ctrl}
 	mock.recorder = &MockBankKeeperMockRecorder{mock}
 	return mock
 }
 
-// EXPECT returns an object that allows the caller to indicate expected use
+// EXPECT returns an object that allows the caller to indicate expected use.
 func (m *MockBankKeeper) EXPECT() *MockBankKeeperMockRecorder {
 	return m.recorder
 }
 
-// SpendableCoins mocks base method
-func (m *MockBankKeeper) SpendableCoins(arg0 types.Context, arg1 types.AccAddress) types.Coins {
+// SpendableCoins mocks base method.
+func (m *MockBankKeeper) SpendableCoins(ctx types.Context, addr types.AccAddress) types.Coins {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "SpendableCoins", arg0, arg1)
+	ret := m.ctrl.Call(m, "SpendableCoins", ctx, addr)
 	ret0, _ := ret[0].(types.Coins)
 	return ret0
 }
 
-// SpendableCoins indicates an expected call of SpendableCoins
-func (mr *MockBankKeeperMockRecorder) SpendableCoins(arg0, arg1 interface{}) *gomock.Call {
+// SpendableCoins indicates an expected call of SpendableCoins.
+func (mr *MockBankKeeperMockRecorder) SpendableCoins(ctx, addr interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpendableCoins", reflect.TypeOf((*MockBankKeeper)(nil).SpendableCoins), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpendableCoins", reflect.TypeOf((*MockBankKeeper)(nil).SpendableCoins), ctx, addr)
 }
diff --git a/x/sp/client/cli/flags.go b/x/sp/client/cli/flags.go
index 2813a16d8..011157f83 100644
--- a/x/sp/client/cli/flags.go
+++ b/x/sp/client/cli/flags.go
@@ -6,6 +6,8 @@ const (
 	FlagOperatorAddress = "operator-address"
 	FlagFundingAddress  = "funding-address"
 	FlagSealAddress     = "seal-address"
+	FlagBlsPubKey       = "bls-pub-key"
+	FlagBlsProof        = "bls-proof"
 	FlagApprovalAddress = "approval-address"
 	FlagGcAddress       = "gc-address"
 	FlagCreator         = "creator"
diff --git a/x/sp/client/cli/query.go b/x/sp/client/cli/query.go
index 1aebeea09..8972c6b39 100644
--- a/x/sp/client/cli/query.go
+++ b/x/sp/client/cli/query.go
@@ -2,9 +2,11 @@ package cli
 
 import (
 	"fmt"
+	"strconv"
 
 	"github.com/cosmos/cosmos-sdk/client"
 	"github.com/cosmos/cosmos-sdk/client/flags"
+	sdk "github.com/cosmos/cosmos-sdk/types"
 	"github.com/spf13/cobra"
 
 	"github.com/bnb-chain/greenfield/x/sp/types"
@@ -13,7 +15,7 @@ import (
 // GetQueryCmd returns the cli query commands for this module
 func GetQueryCmd(queryRoute string) *cobra.Command {
 	// Group sp queries under a subcommand
-	spQueryCmd := &cobra.Command{
+	cmd := &cobra.Command{
 		Use:                        types.ModuleName,
 		Short:                      fmt.Sprintf("Querying commands for the %s module", types.ModuleName),
 		DisableFlagParsing:         true,
@@ -21,15 +23,15 @@ func GetQueryCmd(queryRoute string) *cobra.Command {
 		RunE:                       client.ValidateCmd,
 	}
 
-	spQueryCmd.AddCommand(
+	cmd.AddCommand(
 		CmdQueryParams(),
 		CmdStorageProviders(),
 		CmdStorageProvider(),
+		CmdStorageProviderByOperatorAddress(),
 	)
 
 	// this line is used by starport scaffolding # 1
-
-	return spQueryCmd
+	return cmd
 }
 
 func CmdStorageProviders() *cobra.Command {
@@ -64,11 +66,15 @@ func CmdStorageProviders() *cobra.Command {
 
 func CmdStorageProvider() *cobra.Command {
 	cmd := &cobra.Command{
-		Use:   "storage-provider [sp-operator-address]",
+		Use:   "storage-provider [sp-id]",
 		Short: "Query storage provider with specify operator address",
 		Args:  cobra.ExactArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) (err error) {
-			reqSpAddress := args[0]
+			reqSpID := args[0]
+			spID, err := strconv.ParseUint(reqSpID, 10, 32)
+			if err != nil {
+				return err
+			}
 
 			clientCtx, err := client.GetClientQueryContext(cmd)
 			if err != nil {
@@ -77,7 +83,7 @@ func CmdStorageProvider() *cobra.Command {
 
 			queryClient := types.NewQueryClient(clientCtx)
 			params := &types.QueryStorageProviderRequest{
-				SpAddress: reqSpAddress,
+				Id: uint32(spID),
 			}
 
 			res, err := queryClient.StorageProvider(cmd.Context(), params)
@@ -93,3 +99,41 @@ func CmdStorageProvider() *cobra.Command {
 
 	return cmd
 }
+
+func CmdStorageProviderByOperatorAddress() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "storage-provider-by-operator-address [operator address]",
+		Short: "Query StorageProviderByOperatorAddress",
+		Args:  cobra.ExactArgs(0),
+		RunE: func(cmd *cobra.Command, args []string) (err error) {
+			reqSpAddr := args[0]
+
+			operatorAddr, err := sdk.AccAddressFromHexUnsafe(reqSpAddr)
+			if err != nil {
+				return err
+			}
+
+			clientCtx, err := client.GetClientQueryContext(cmd)
+			if err != nil {
+				return err
+			}
+
+			queryClient := types.NewQueryClient(clientCtx)
+
+			params := &types.QueryStorageProviderByOperatorAddressRequest{
+				OperatorAddress: operatorAddr.String(),
+			}
+
+			res, err := queryClient.StorageProviderByOperatorAddress(cmd.Context(), params)
+			if err != nil {
+				return err
+			}
+
+			return clientCtx.PrintProto(res)
+		},
+	}
+
+	flags.AddQueryFlagsToCmd(cmd)
+
+	return cmd
+}
diff --git a/x/sp/client/cli/tx.go b/x/sp/client/cli/tx.go
index 588689a36..87c2c793a 100644
--- a/x/sp/client/cli/tx.go
+++ b/x/sp/client/cli/tx.go
@@ -97,6 +97,18 @@ func CmdEditStorageProvider() *cobra.Command {
 				}
 			}
 
+			// bls key
+			blsPubKey, err := cmd.Flags().GetString(FlagBlsPubKey)
+			if err != nil {
+				return err
+			}
+			if len(blsPubKey) != 2*sdk.BLSPubKeyLength {
+				return fmt.Errorf("invalid bls pubkey")
+			}
+
+			// bls proof
+			blsProof, _ := cmd.Flags().GetString(FlagBlsProof)
+
 			clientCtx, err := client.GetClientTxContext(cmd)
 			if err != nil {
 				return err
@@ -109,6 +121,8 @@ func CmdEditStorageProvider() *cobra.Command {
 				sealAddress,
 				approvalAddress,
 				gcAddress,
+				blsPubKey,
+				blsProof,
 			)
 			if err := msg.ValidateBasic(); err != nil {
 				return err
@@ -126,6 +140,8 @@ func CmdEditStorageProvider() *cobra.Command {
 	cmd.Flags().String(FlagDetails, types.DoNotModifyDesc, "The storage provider's (optional) details")
 
 	cmd.Flags().String(FlagSealAddress, "", "The seal address of storage provider")
+	cmd.Flags().String(FlagBlsPubKey, "", "The Bls public key of storage provider")
+	cmd.Flags().String(FlagBlsProof, "", "The Bls signature of storage provider signing the bls pub key")
 	cmd.Flags().String(FlagApprovalAddress, "", "The approval address of storage provider")
 	cmd.Flags().String(FlagGcAddress, "", "The gc address of storage provider")
 
@@ -288,6 +304,8 @@ func CreateStorageProviderMsgFlagSet(ipDefault string) (fs *flag.FlagSet, defaul
 	fsCreateStorageProvider.String(FlagOperatorAddress, "", "The operator address of storage provider")
 	fsCreateStorageProvider.String(FlagFundingAddress, "", "The funding address of storage provider")
 	fsCreateStorageProvider.String(FlagSealAddress, "", "The seal address of storage provider")
+	fsCreateStorageProvider.String(FlagBlsPubKey, "", "The bls public key of storage provider")
+	fsCreateStorageProvider.String(FlagBlsProof, "", "The Bls signature of storage provider signing the bls pub key")
 	fsCreateStorageProvider.String(FlagApprovalAddress, "", "The approval address of storage provider")
 	fsCreateStorageProvider.String(FlagGcAddress, "", "The gc address of storage provider")
 
@@ -316,6 +334,8 @@ type TxCreateStorageProviderConfig struct {
 	SpAddress       sdk.AccAddress
 	FundingAddress  sdk.AccAddress
 	SealAddress     sdk.AccAddress
+	BlsPubKey       string
+	BlsProof        string
 	ApprovalAddress sdk.AccAddress
 	GcAddress       sdk.AccAddress
 
@@ -407,6 +427,23 @@ func PrepareConfigForTxCreateStorageProvider(flagSet *flag.FlagSet) (TxCreateSto
 	}
 	c.SealAddress = addr
 
+	// bls key
+	blsPubKey, err := flagSet.GetString(FlagBlsPubKey)
+	if err != nil {
+		return c, err
+	}
+	if len(blsPubKey) != 2*sdk.BLSPubKeyLength {
+		return c, fmt.Errorf("invalid bls pubkey")
+	}
+	c.BlsPubKey = blsPubKey
+
+	// bls proof
+	blsProof, err := flagSet.GetString(FlagBlsProof)
+	if err != nil {
+		return c, err
+	}
+	c.BlsProof = blsProof
+
 	// approval address
 	approvalAddress, err := flagSet.GetString(FlagApprovalAddress)
 	if err != nil {
@@ -484,6 +521,7 @@ func BuildCreateStorageProviderMsg(config TxCreateStorageProviderConfig, txBldr
 		config.Creator, config.SpAddress, config.FundingAddress,
 		config.SealAddress, config.ApprovalAddress, config.GcAddress, description,
 		config.Endpoint, deposit, config.ReadPrice, config.FreeReadQuota, config.StorePrice,
+		config.BlsPubKey, config.BlsProof,
 	)
 	if err != nil {
 		return txBldr, msg, err
diff --git a/x/sp/keeper/grpc_query.go b/x/sp/keeper/grpc_query.go
index c14a0605c..dba3f20cd 100644
--- a/x/sp/keeper/grpc_query.go
+++ b/x/sp/keeper/grpc_query.go
@@ -49,7 +49,11 @@ func (k Keeper) QueryGetSpStoragePriceByTime(goCtx context.Context, req *types.Q
 	if err != nil {
 		return nil, status.Error(codes.InvalidArgument, "invalid sp address")
 	}
-	spStoragePrice, err := k.GetSpStoragePriceByTime(ctx, spAddr, req.Timestamp)
+	sp, found := k.GetStorageProviderByOperatorAddr(ctx, spAddr)
+	if !found {
+		return nil, status.Error(codes.InvalidArgument, "unknown sp with the operator address")
+	}
+	spStoragePrice, err := k.GetSpStoragePriceByTime(ctx, sp.Id, req.Timestamp)
 	if err != nil {
 		return nil, status.Errorf(codes.NotFound, "not found, err: %s", err)
 	}
@@ -82,11 +86,7 @@ func (k Keeper) StorageProvider(goCtx context.Context, req *types.QueryStoragePr
 
 	ctx := sdk.UnwrapSDKContext(goCtx)
 
-	spAddr, err := sdk.AccAddressFromHexUnsafe(req.SpAddress)
-	if err != nil {
-		return nil, err
-	}
-	sp, found := k.GetStorageProvider(ctx, spAddr)
+	sp, found := k.GetStorageProvider(ctx, req.Id)
 	if !found {
 		return nil, types.ErrStorageProviderNotFound
 	}
@@ -101,3 +101,21 @@ func (k Keeper) Params(c context.Context, req *types.QueryParamsRequest) (*types
 
 	return &types.QueryParamsResponse{Params: k.GetParams(ctx)}, nil
 }
+
+func (k Keeper) StorageProviderByOperatorAddress(goCtx context.Context, req *types.QueryStorageProviderByOperatorAddressRequest) (*types.QueryStorageProviderByOperatorAddressResponse, error) {
+	if req == nil {
+		return nil, status.Error(codes.InvalidArgument, "invalid request")
+	}
+
+	ctx := sdk.UnwrapSDKContext(goCtx)
+
+	operatorAddr, err := sdk.AccAddressFromHexUnsafe(req.OperatorAddress)
+	if err != nil {
+		return nil, err
+	}
+	sp, found := k.GetStorageProviderByOperatorAddr(ctx, operatorAddr)
+	if !found {
+		return nil, types.ErrStorageProviderNotFound
+	}
+	return &types.QueryStorageProviderByOperatorAddressResponse{StorageProvider: sp}, nil
+}
diff --git a/x/sp/keeper/keeper.go b/x/sp/keeper/keeper.go
index c7b2d6f1c..2855249ff 100644
--- a/x/sp/keeper/keeper.go
+++ b/x/sp/keeper/keeper.go
@@ -8,6 +8,7 @@ import (
 	storetypes "github.com/cosmos/cosmos-sdk/store/types"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 
+	"github.com/bnb-chain/greenfield/internal/sequence"
 	"github.com/bnb-chain/greenfield/x/sp/types"
 )
 
@@ -19,7 +20,8 @@ type (
 		bankKeeper    types.BankKeeper
 		authzKeeper   types.AuthzKeeper
 
-		authority string
+		spSequence sequence.Sequence[uint32]
+		authority  string
 	}
 )
 
@@ -33,7 +35,7 @@ func NewKeeper(
 
 ) *Keeper {
 
-	return &Keeper{
+	k := &Keeper{
 		cdc:           cdc,
 		storeKey:      key,
 		accountKeeper: ak,
@@ -41,6 +43,9 @@ func NewKeeper(
 		authzKeeper:   azk,
 		authority:     authority,
 	}
+
+	k.spSequence = sequence.NewSequence[uint32](types.StorageProviderSequenceKey)
+	return k
 }
 
 func (k Keeper) GetAuthority() string {
@@ -50,3 +55,10 @@ func (k Keeper) GetAuthority() string {
 func (k Keeper) Logger(ctx sdk.Context) log.Logger {
 	return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
 }
+
+func (k Keeper) GetNextSpID(ctx sdk.Context) uint32 {
+	store := ctx.KVStore(k.storeKey)
+
+	seq := k.spSequence.NextVal(store)
+	return seq
+}
diff --git a/x/sp/keeper/msg_server.go b/x/sp/keeper/msg_server.go
index f751687aa..fd013c855 100644
--- a/x/sp/keeper/msg_server.go
+++ b/x/sp/keeper/msg_server.go
@@ -2,15 +2,20 @@ package keeper
 
 import (
 	"context"
+	"encoding/hex"
 
 	"cosmossdk.io/errors"
 	errorsmod "cosmossdk.io/errors"
+	"github.com/cometbft/cometbft/crypto/tmhash"
 	sdk "github.com/cosmos/cosmos-sdk/types"
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 	gov "github.com/cosmos/cosmos-sdk/x/gov/types"
 	govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
+	"github.com/prysmaticlabs/prysm/crypto/bls"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
 
+	gnfderrors "github.com/bnb-chain/greenfield/types/errors"
 	"github.com/bnb-chain/greenfield/x/sp/types"
 )
 
@@ -53,7 +58,7 @@ func (k msgServer) CreateStorageProvider(goCtx context.Context, msg *types.MsgCr
 		}
 	}
 
-	if _, found := k.GetStorageProvider(ctx, spAcc); found {
+	if _, found := k.GetStorageProviderByOperatorAddr(ctx, spAcc); found {
 		return nil, types.ErrStorageProviderOwnerExists
 	}
 
@@ -77,10 +82,20 @@ func (k msgServer) CreateStorageProvider(goCtx context.Context, msg *types.MsgCr
 		return nil, types.ErrStorageProviderGcAddrExists
 	}
 
-	if err := msg.Description.EnsureLength(); err != nil {
+	// check if the bls pubkey has been registered before
+	blsPk, err := hex.DecodeString(msg.BlsKey)
+	if err != nil || len(blsPk) != sdk.BLSPubKeyLength {
+		return nil, types.ErrStorageProviderInvalidBlsKey
+	}
+	if _, found := k.GetStorageProviderByBlsKey(ctx, blsPk); found {
+		return nil, types.ErrStorageProviderBlsKeyExists
+	}
+	if err = k.checkBlsProof(blsPk, msg.BlsProof); err != nil {
+		return nil, err
+	}
+	if err = msg.Description.EnsureLength(); err != nil {
 		return nil, err
 	}
-
 	if msg.Deposit.Amount.LT(k.MinDeposit(ctx)) {
 		return nil, types.ErrInsufficientDepositAmount
 	}
@@ -108,21 +123,23 @@ func (k msgServer) CreateStorageProvider(goCtx context.Context, msg *types.MsgCr
 		return nil, err
 	}
 
-	sp, err := types.NewStorageProvider(spAcc, fundingAcc, sealAcc, approvalAcc, gcAcc,
-		msg.Deposit.Amount, msg.Endpoint, msg.Description)
+	sp, err := types.NewStorageProvider(k.GetNextSpID(ctx), spAcc, fundingAcc, sealAcc, approvalAcc, gcAcc,
+		msg.Deposit.Amount, msg.Endpoint, msg.Description, msg.BlsKey)
 	if err != nil {
 		return nil, err
 	}
 
 	k.SetStorageProvider(ctx, &sp)
+	k.SetStorageProviderByOperatorAddr(ctx, &sp)
 	k.SetStorageProviderByApprovalAddr(ctx, &sp)
 	k.SetStorageProviderByFundingAddr(ctx, &sp)
 	k.SetStorageProviderBySealAddr(ctx, &sp)
 	k.SetStorageProviderByGcAddr(ctx, &sp)
+	k.SetStorageProviderByBlsKey(ctx, &sp)
 
 	// set initial sp storage price
 	spStoragePrice := types.SpStoragePrice{
-		SpAddress:     spAcc.String(),
+		SpId:          sp.Id,
 		UpdateTimeSec: ctx.BlockTime().Unix(),
 		ReadPrice:     msg.ReadPrice,
 		StorePrice:    msg.StorePrice,
@@ -135,6 +152,7 @@ func (k msgServer) CreateStorageProvider(goCtx context.Context, msg *types.MsgCr
 	}
 
 	if err = ctx.EventManager().EmitTypedEvents(&types.EventCreateStorageProvider{
+		SpId:            sp.Id,
 		SpAddress:       spAcc.String(),
 		FundingAddress:  fundingAcc.String(),
 		SealAddress:     sealAcc.String(),
@@ -144,6 +162,7 @@ func (k msgServer) CreateStorageProvider(goCtx context.Context, msg *types.MsgCr
 		TotalDeposit:    &msg.Deposit,
 		Status:          sp.Status,
 		Description:     sp.Description,
+		BlsKey:          hex.EncodeToString(sp.BlsKey),
 	}); err != nil {
 		return nil, err
 	}
@@ -154,9 +173,9 @@ func (k msgServer) CreateStorageProvider(goCtx context.Context, msg *types.MsgCr
 func (k msgServer) EditStorageProvider(goCtx context.Context, msg *types.MsgEditStorageProvider) (*types.MsgEditStorageProviderResponse, error) {
 	ctx := sdk.UnwrapSDKContext(goCtx)
 
-	spAcc := sdk.MustAccAddressFromHex(msg.SpAddress)
+	operatorAcc := sdk.MustAccAddressFromHex(msg.SpAddress)
 
-	sp, found := k.GetStorageProvider(ctx, spAcc)
+	sp, found := k.GetStorageProviderByOperatorAddr(ctx, operatorAcc)
 	if !found {
 		return nil, types.ErrStorageProviderNotFound
 	}
@@ -196,6 +215,18 @@ func (k msgServer) EditStorageProvider(goCtx context.Context, msg *types.MsgEdit
 		changed = true
 	}
 
+	if msg.BlsKey != "" && len(msg.BlsProof) != 0 {
+		blsPk, err := hex.DecodeString(msg.BlsKey)
+		if err != nil || len(blsPk) != sdk.BLSPubKeyLength {
+			return nil, types.ErrStorageProviderInvalidBlsKey
+		}
+		if err = k.checkBlsProof(blsPk, msg.BlsProof); err != nil {
+			return nil, err
+		}
+		sp.BlsKey = blsPk
+		changed = true
+	}
+
 	if !changed {
 		return nil, types.ErrStorageProviderNotChanged
 	}
@@ -205,14 +236,17 @@ func (k msgServer) EditStorageProvider(goCtx context.Context, msg *types.MsgEdit
 	k.SetStorageProviderBySealAddr(ctx, sp)
 	k.SetStorageProviderByApprovalAddr(ctx, sp)
 	k.SetStorageProviderByGcAddr(ctx, sp)
+	k.SetStorageProviderByBlsKey(ctx, sp)
 
 	if err := ctx.EventManager().EmitTypedEvents(&types.EventEditStorageProvider{
-		SpAddress:       spAcc.String(),
+		SpId:            sp.Id,
+		SpAddress:       operatorAcc.String(),
 		Endpoint:        sp.Endpoint,
 		Description:     sp.Description,
 		ApprovalAddress: sp.ApprovalAddress,
 		SealAddress:     sp.SealAddress,
 		GcAddress:       sp.GcAddress,
+		BlsKey:          hex.EncodeToString(sp.BlsKey),
 	}); err != nil {
 		return nil, err
 	}
@@ -262,21 +296,26 @@ func (k msgServer) Deposit(goCtx context.Context, msg *types.MsgDeposit) (*types
 func (k msgServer) UpdateSpStoragePrice(goCtx context.Context, msg *types.MsgUpdateSpStoragePrice) (*types.MsgUpdateSpStoragePriceResponse, error) {
 	ctx := sdk.UnwrapSDKContext(goCtx)
 	spAcc := sdk.MustAccAddressFromHex(msg.SpAddress)
-	err := k.IsStorageProviderExistAndInService(ctx, spAcc)
-	if err != nil {
-		return nil, errors.Wrapf(err, "IsStorageProviderExistAndInService return err")
+
+	sp, found := k.GetStorageProviderByOperatorAddr(ctx, spAcc)
+	if !found {
+		return nil, types.ErrStorageProviderNotFound
+	}
+
+	if sp.Status != types.STATUS_IN_SERVICE {
+		return nil, types.ErrStorageProviderNotInService
 	}
 
 	current := ctx.BlockTime().Unix()
 	spStorePrice := types.SpStoragePrice{
 		UpdateTimeSec: current,
-		SpAddress:     spAcc.String(),
+		SpId:          sp.Id,
 		ReadPrice:     msg.ReadPrice,
 		StorePrice:    msg.StorePrice,
 		FreeReadQuota: msg.FreeReadQuota,
 	}
 	k.SetSpStoragePrice(ctx, spStorePrice)
-	err = k.UpdateSecondarySpStorePrice(ctx)
+	err := k.UpdateSecondarySpStorePrice(ctx)
 	if err != nil {
 		return nil, errors.Wrapf(err, "update secondary sp store price failed")
 	}
@@ -295,3 +334,27 @@ func (k msgServer) UpdateParams(goCtx context.Context, req *types.MsgUpdateParam
 
 	return &types.MsgUpdateParamsResponse{}, nil
 }
+
+// checkBlsProof checks the BLS signature of the Storage Provider
+func (k msgServer) checkBlsProof(blsPk []byte, sig string) error {
+	// check to see if the bls proof is signed from sp
+	blsProof, err := hex.DecodeString(sig)
+	if err != nil {
+		return gnfderrors.ErrInvalidBlsSignature
+	}
+	if len(blsProof) != sdk.BLSSignatureLength {
+		return gnfderrors.ErrInvalidBlsSignature
+	}
+	signature, err := bls.SignatureFromBytes(blsProof)
+	if err != nil {
+		return gnfderrors.ErrInvalidBlsSignature
+	}
+	blsPubKey, err := bls.PublicKeyFromBytes(blsPk)
+	if err != nil {
+		return types.ErrStorageProviderInvalidBlsKey
+	}
+	if !signature.Verify(blsPubKey, tmhash.Sum(blsPk)) {
+		return sdkerrors.ErrorInvalidSigner
+	}
+	return nil
+}
diff --git a/x/sp/keeper/msg_server_test.go b/x/sp/keeper/msg_server_test.go
index da8962ad6..e0085691c 100644
--- a/x/sp/keeper/msg_server_test.go
+++ b/x/sp/keeper/msg_server_test.go
@@ -31,6 +31,8 @@ func (s *KeeperTestSuite) TestMsgCreateStorageProvider() {
 	gcAddr, _, err := testutil.GenerateCoinKey(hd.Secp256k1, s.cdc)
 	s.Require().Nil(err, "error should be nil")
 
+	blsPubKeyHex := sample.RandBlsPubKeyHex()
+
 	s.accountKeeper.EXPECT().GetAccount(gomock.Any(), fundingAddr).Return(authtypes.NewBaseAccountWithAddress(fundingAddr)).AnyTimes()
 	s.accountKeeper.EXPECT().GetAccount(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
 	s.bankKeeper.EXPECT().SendCoinsFromAccountToModule(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
@@ -54,6 +56,7 @@ func (s *KeeperTestSuite) TestMsgCreateStorageProvider() {
 				SealAddress:     sealAddr.String(),
 				ApprovalAddress: approvalAddr.String(),
 				GcAddress:       gcAddr.String(),
+				BlsKey:          blsPubKeyHex,
 				Deposit: sdk.Coin{
 					Denom:  types.Denom,
 					Amount: types.NewIntFromInt64WithDecimal(10000, types.DecimalBNB),
@@ -74,6 +77,29 @@ func (s *KeeperTestSuite) TestMsgCreateStorageProvider() {
 				SealAddress:     sealAddr.String(),
 				ApprovalAddress: approvalAddr.String(),
 				GcAddress:       gcAddr.String(),
+				BlsKey:          blsPubKeyHex,
+				Endpoint:        "sp.io",
+				Deposit: sdk.Coin{
+					Denom:  types.Denom,
+					Amount: types.NewIntFromInt64WithDecimal(10000, types.DecimalBNB),
+				},
+			},
+		},
+		{
+			Name:      "invalid bls pub key",
+			ExceptErr: true,
+			req: types.MsgCreateStorageProvider{
+				Creator: govAddr.String(),
+				Description: sptypes.Description{
+					Moniker:  "sp_test",
+					Identity: "",
+				},
+				SpAddress:       operatorAddr.String(),
+				FundingAddress:  fundingAddr.String(),
+				SealAddress:     sealAddr.String(),
+				ApprovalAddress: approvalAddr.String(),
+				GcAddress:       gcAddr.String(),
+				BlsKey:          "InValidBlsPubkey",
 				Endpoint:        "sp.io",
 				Deposit: sdk.Coin{
 					Denom:  types.Denom,
@@ -95,6 +121,7 @@ func (s *KeeperTestSuite) TestMsgCreateStorageProvider() {
 				SealAddress:     sealAddr.String(),
 				ApprovalAddress: approvalAddr.String(),
 				GcAddress:       gcAddr.String(),
+				BlsKey:          blsPubKeyHex,
 				Deposit: sdk.Coin{
 					Denom:  types.Denom,
 					Amount: types.NewIntFromInt64WithDecimal(10000, types.DecimalBNB),
diff --git a/x/sp/keeper/slash.go b/x/sp/keeper/slash.go
index 96c9cdc08..2a23af7da 100644
--- a/x/sp/keeper/slash.go
+++ b/x/sp/keeper/slash.go
@@ -7,8 +7,8 @@ import (
 	"github.com/bnb-chain/greenfield/x/sp/types"
 )
 
-func (k Keeper) Slash(ctx sdk.Context, spAcc sdk.AccAddress, rewardInfos []types.RewardInfo) error {
-	sp, found := k.GetStorageProvider(ctx, spAcc)
+func (k Keeper) Slash(ctx sdk.Context, spID uint32, rewardInfos []types.RewardInfo) error {
+	sp, found := k.GetStorageProvider(ctx, spID)
 	if !found {
 		return types.ErrStorageProviderNotFound
 	}
diff --git a/x/sp/keeper/sp.go b/x/sp/keeper/sp.go
index 36c012ad7..e79321964 100644
--- a/x/sp/keeper/sp.go
+++ b/x/sp/keeper/sp.go
@@ -7,106 +7,117 @@ import (
 	"github.com/bnb-chain/greenfield/x/sp/types"
 )
 
-func (k Keeper) IsStorageProviderExistAndInService(ctx sdk.Context, addr sdk.AccAddress) error {
+func (k Keeper) GetStorageProvider(ctx sdk.Context, id uint32) (sp *types.StorageProvider, found bool) {
 	store := ctx.KVStore(k.storeKey)
 
-	value := store.Get(types.GetStorageProviderKey(addr))
+	value := store.Get(types.GetStorageProviderKey(k.spSequence.EncodeSequence(id)))
 	if value == nil {
-		return types.ErrStorageProviderNotFound
+		return sp, false
 	}
 
-	sp := types.MustUnmarshalStorageProvider(k.cdc, value)
-	if sp.Status != types.STATUS_IN_SERVICE {
-		return types.ErrStorageProviderNotInService
+	sp = types.MustUnmarshalStorageProvider(k.cdc, value)
+	return sp, true
+}
+
+func (k Keeper) MustGetStorageProvider(ctx sdk.Context, id uint32) *types.StorageProvider {
+	sp, found := k.GetStorageProvider(ctx, id)
+	if !found {
+		panic("must get storage provider, but it not exist")
 	}
-	return nil
+	return sp
 }
 
-func (k Keeper) GetStorageProvider(ctx sdk.Context, addr sdk.AccAddress) (sp *types.StorageProvider, found bool) {
+func (k Keeper) GetStorageProviderByOperatorAddr(ctx sdk.Context, opAddr sdk.AccAddress) (sp *types.StorageProvider, found bool) {
 	store := ctx.KVStore(k.storeKey)
 
-	value := store.Get(types.GetStorageProviderKey(addr))
-	if value == nil {
+	id := store.Get(types.GetStorageProviderByOperatorAddrKey(opAddr))
+	if id == nil {
 		return sp, false
 	}
 
-	sp = types.MustUnmarshalStorageProvider(k.cdc, value)
-	return sp, true
+	return k.GetStorageProvider(ctx, k.spSequence.DecodeSequence(id))
 }
 
 func (k Keeper) GetStorageProviderByFundingAddr(ctx sdk.Context, fundAddr sdk.AccAddress) (sp *types.StorageProvider, found bool) {
 	store := ctx.KVStore(k.storeKey)
 
-	spAddr := store.Get(types.GetStorageProviderByFundingAddrKey(fundAddr))
-	if spAddr == nil {
+	id := store.Get(types.GetStorageProviderByFundingAddrKey(fundAddr))
+	if id == nil {
 		return sp, false
 	}
 
-	return k.GetStorageProvider(ctx, spAddr)
+	return k.GetStorageProvider(ctx, k.spSequence.DecodeSequence(id))
 }
 
 func (k Keeper) GetStorageProviderBySealAddr(ctx sdk.Context, sealAddr sdk.AccAddress) (sp *types.StorageProvider, found bool) {
 	store := ctx.KVStore(k.storeKey)
 
-	spAddr := store.Get(types.GetStorageProviderBySealAddrKey(sealAddr))
-	if spAddr == nil {
+	id := store.Get(types.GetStorageProviderBySealAddrKey(sealAddr))
+	if id == nil {
 		return sp, false
 	}
 
-	return k.GetStorageProvider(ctx, spAddr)
+	return k.GetStorageProvider(ctx, k.spSequence.DecodeSequence(id))
 }
 
 func (k Keeper) GetStorageProviderByApprovalAddr(ctx sdk.Context, approvalAddr sdk.AccAddress) (sp *types.StorageProvider, found bool) {
 	store := ctx.KVStore(k.storeKey)
 
-	spAddr := store.Get(types.GetStorageProviderByApprovalAddrKey(approvalAddr))
-	if spAddr == nil {
+	id := store.Get(types.GetStorageProviderByApprovalAddrKey(approvalAddr))
+	if id == nil {
 		return sp, false
 	}
 
-	return k.GetStorageProvider(ctx, spAddr)
+	return k.GetStorageProvider(ctx, k.spSequence.DecodeSequence(id))
 }
 
 func (k Keeper) GetStorageProviderByGcAddr(ctx sdk.Context, gcAddr sdk.AccAddress) (sp *types.StorageProvider, found bool) {
 	store := ctx.KVStore(k.storeKey)
 
-	spAddr := store.Get(types.GetStorageProviderByGcAddrKey(gcAddr))
-	if spAddr == nil {
+	id := store.Get(types.GetStorageProviderByGcAddrKey(gcAddr))
+	if id == nil {
 		return sp, false
 	}
 
-	return k.GetStorageProvider(ctx, spAddr)
+	return k.GetStorageProvider(ctx, k.spSequence.DecodeSequence(id))
 }
 
 func (k Keeper) SetStorageProvider(ctx sdk.Context, sp *types.StorageProvider) {
 	store := ctx.KVStore(k.storeKey)
 	bz := types.MustMarshalStorageProvider(k.cdc, sp)
 
-	store.Set(types.GetStorageProviderKey(sp.GetOperator()), bz)
+	store.Set(types.GetStorageProviderKey(k.spSequence.EncodeSequence(sp.Id)), bz)
+}
+
+func (k Keeper) SetStorageProviderByOperatorAddr(ctx sdk.Context, sp *types.StorageProvider) {
+	operatorAddr := sp.GetOperatorAccAddress()
+	store := ctx.KVStore(k.storeKey)
+
+	store.Set(types.GetStorageProviderByOperatorAddrKey(operatorAddr), k.spSequence.EncodeSequence(sp.Id))
 }
 
 func (k Keeper) SetStorageProviderByFundingAddr(ctx sdk.Context, sp *types.StorageProvider) {
 	fundAddr := sp.GetFundingAccAddress()
 	store := ctx.KVStore(k.storeKey)
-	store.Set(types.GetStorageProviderByFundingAddrKey(fundAddr), sp.GetOperator())
+	store.Set(types.GetStorageProviderByFundingAddrKey(fundAddr), k.spSequence.EncodeSequence(sp.Id))
 }
 
 func (k Keeper) SetStorageProviderBySealAddr(ctx sdk.Context, sp *types.StorageProvider) {
 	sealAddr := sp.GetSealAccAddress()
 	store := ctx.KVStore(k.storeKey)
-	store.Set(types.GetStorageProviderBySealAddrKey(sealAddr), sp.GetOperator())
+	store.Set(types.GetStorageProviderBySealAddrKey(sealAddr), k.spSequence.EncodeSequence(sp.Id))
 }
 
 func (k Keeper) SetStorageProviderByApprovalAddr(ctx sdk.Context, sp *types.StorageProvider) {
 	approvalAddr := sp.GetApprovalAccAddress()
 	store := ctx.KVStore(k.storeKey)
-	store.Set(types.GetStorageProviderByApprovalAddrKey(approvalAddr), sp.GetOperator())
+	store.Set(types.GetStorageProviderByApprovalAddrKey(approvalAddr), k.spSequence.EncodeSequence(sp.Id))
 }
 
 func (k Keeper) SetStorageProviderByGcAddr(ctx sdk.Context, sp *types.StorageProvider) {
 	gcAddr := sp.GetGcAccAddress()
 	store := ctx.KVStore(k.storeKey)
-	store.Set(types.GetStorageProviderByGcAddrKey(gcAddr), sp.GetOperator())
+	store.Set(types.GetStorageProviderByGcAddrKey(gcAddr), k.spSequence.EncodeSequence(sp.Id))
 }
 
 func (k Keeper) GetAllStorageProviders(ctx sdk.Context) (sps []types.StorageProvider) {
@@ -120,3 +131,30 @@ func (k Keeper) GetAllStorageProviders(ctx sdk.Context) (sps []types.StorageProv
 	}
 	return sps
 }
+
+func (k Keeper) Exit(ctx sdk.Context, sp *types.StorageProvider) error {
+	store := ctx.KVStore(k.storeKey)
+	store.Delete(types.GetStorageProviderByOperatorAddrKey(sdk.MustAccAddressFromHex(sp.OperatorAddress)))
+	store.Delete(types.GetStorageProviderByFundingAddrKey(sdk.MustAccAddressFromHex(sp.FundingAddress)))
+	store.Delete(types.GetStorageProviderBySealAddrKey(sdk.MustAccAddressFromHex(sp.SealAddress)))
+	store.Delete(types.GetStorageProviderByApprovalAddrKey(sdk.MustAccAddressFromHex(sp.ApprovalAddress)))
+	store.Delete(types.GetStorageProviderByGcAddrKey(sdk.MustAccAddressFromHex(sp.GcAddress)))
+	store.Delete(types.GetStorageProviderKey(k.spSequence.EncodeSequence(sp.Id)))
+	store.Delete(types.GetStorageProviderByBlsKeyKey(types.GetStorageProviderByBlsKeyKey(sp.GetBlsKey())))
+	return nil
+}
+
+func (k Keeper) SetStorageProviderByBlsKey(ctx sdk.Context, sp *types.StorageProvider) {
+	blsPk := sp.GetBlsKey()
+	store := ctx.KVStore(k.storeKey)
+	store.Set(types.GetStorageProviderByBlsKeyKey(blsPk), k.spSequence.EncodeSequence(sp.Id))
+}
+
+func (k Keeper) GetStorageProviderByBlsKey(ctx sdk.Context, blsPk []byte) (sp *types.StorageProvider, found bool) {
+	store := ctx.KVStore(k.storeKey)
+	id := store.Get(types.GetStorageProviderByBlsKeyKey(blsPk))
+	if id == nil {
+		return sp, false
+	}
+	return k.GetStorageProvider(ctx, k.spSequence.DecodeSequence(id))
+}
diff --git a/x/sp/keeper/sp_storage_price.go b/x/sp/keeper/sp_storage_price.go
index 79297015b..22f1242e9 100644
--- a/x/sp/keeper/sp_storage_price.go
+++ b/x/sp/keeper/sp_storage_price.go
@@ -12,22 +12,22 @@ import (
 )
 
 // SetSpStoragePrice set a specific SpStoragePrice in the store from its index
-func (k Keeper) SetSpStoragePrice(ctx sdk.Context, SpStoragePrice types.SpStoragePrice) {
+func (k Keeper) SetSpStoragePrice(ctx sdk.Context, spStoragePrice types.SpStoragePrice) {
 	event := &types.EventSpStoragePriceUpdate{
-		SpAddress:     SpStoragePrice.SpAddress,
-		UpdateTimeSec: SpStoragePrice.UpdateTimeSec,
-		ReadPrice:     SpStoragePrice.ReadPrice,
-		StorePrice:    SpStoragePrice.StorePrice,
-		FreeReadQuota: SpStoragePrice.FreeReadQuota,
+		SpId:          spStoragePrice.SpId,
+		UpdateTimeSec: spStoragePrice.UpdateTimeSec,
+		ReadPrice:     spStoragePrice.ReadPrice,
+		StorePrice:    spStoragePrice.StorePrice,
+		FreeReadQuota: spStoragePrice.FreeReadQuota,
 	}
 	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.SpStoragePriceKeyPrefix)
 	key := types.SpStoragePriceKey(
-		SpStoragePrice.GetSpAccAddress(),
-		SpStoragePrice.UpdateTimeSec,
+		spStoragePrice.SpId,
+		spStoragePrice.UpdateTimeSec,
 	)
-	SpStoragePrice.UpdateTimeSec = 0
-	SpStoragePrice.SpAddress = ""
-	b := k.cdc.MustMarshal(&SpStoragePrice)
+	spStoragePrice.UpdateTimeSec = 0
+	spStoragePrice.SpId = 0
+	b := k.cdc.MustMarshal(&spStoragePrice)
 	store.Set(key, b)
 	_ = ctx.EventManager().EmitTypedEvents(event)
 }
@@ -35,22 +35,22 @@ func (k Keeper) SetSpStoragePrice(ctx sdk.Context, SpStoragePrice types.SpStorag
 // GetSpStoragePrice returns a SpStoragePrice from its index
 func (k Keeper) GetSpStoragePrice(
 	ctx sdk.Context,
-	spAddr sdk.AccAddress,
-	UpdateTimeSec int64,
+	spId uint32,
+	timestamp int64,
 ) (val types.SpStoragePrice, found bool) {
 	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.SpStoragePriceKeyPrefix)
 
 	b := store.Get(types.SpStoragePriceKey(
-		spAddr,
-		UpdateTimeSec,
+		spId,
+		timestamp,
 	))
 	if b == nil {
 		return val, false
 	}
 
 	k.cdc.MustUnmarshal(b, &val)
-	val.SpAddress = spAddr.String()
-	val.UpdateTimeSec = UpdateTimeSec
+	val.SpId = spId
+	val.UpdateTimeSec = timestamp
 	return val, true
 }
 
@@ -64,9 +64,9 @@ func (k Keeper) GetAllSpStoragePrice(ctx sdk.Context) (list []types.SpStoragePri
 	for ; iterator.Valid(); iterator.Next() {
 		var val types.SpStoragePrice
 		k.cdc.MustUnmarshal(iterator.Value(), &val)
-		spAddr, UpdateTimeSec := types.ParseSpStoragePriceKey(iterator.Key())
-		val.SpAddress = spAddr.String()
-		val.UpdateTimeSec = UpdateTimeSec
+		spId, timestamp := types.ParseSpStoragePriceKey(iterator.Key())
+		val.SpId = spId
+		val.UpdateTimeSec = timestamp
 		list = append(list, val)
 	}
 
@@ -76,14 +76,14 @@ func (k Keeper) GetAllSpStoragePrice(ctx sdk.Context) (list []types.SpStoragePri
 // GetSpStoragePriceByTime find the latest price before the given time
 func (k Keeper) GetSpStoragePriceByTime(
 	ctx sdk.Context,
-	spAddr sdk.AccAddress,
-	time int64,
+	spId uint32,
+	timestamp int64,
 ) (val types.SpStoragePrice, err error) {
 	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.SpStoragePriceKeyPrefix)
 
 	startKey := types.SpStoragePriceKey(
-		spAddr,
-		time,
+		spId,
+		timestamp,
 	)
 	iterator := store.ReverseIterator(nil, startKey)
 	defer iterator.Close()
@@ -91,13 +91,13 @@ func (k Keeper) GetSpStoragePriceByTime(
 		return val, fmt.Errorf("no price found")
 	}
 
-	spAddrRes, UpdateTimeSec := types.ParseSpStoragePriceKey(iterator.Key())
-	if !spAddrRes.Equals(spAddr) {
+	resSpId, resTimestamp := types.ParseSpStoragePriceKey(iterator.Key())
+	if resSpId != spId {
 		return val, fmt.Errorf("no price found")
 	}
 	k.cdc.MustUnmarshal(iterator.Value(), &val)
-	val.SpAddress = spAddr.String()
-	val.UpdateTimeSec = UpdateTimeSec
+	val.SpId = spId
+	val.UpdateTimeSec = resTimestamp
 
 	return val, nil
 }
@@ -126,7 +126,7 @@ func (k Keeper) UpdateSecondarySpStorePrice(ctx sdk.Context) error {
 		if sp.Status != types.STATUS_IN_SERVICE {
 			continue
 		}
-		price, err := k.GetSpStoragePriceByTime(ctx, sp.GetOperator(), current+1)
+		price, err := k.GetSpStoragePriceByTime(ctx, sp.Id, current+1)
 		if err != nil {
 			return err
 		}
@@ -166,7 +166,7 @@ func (k Keeper) GetSecondarySpStorePriceByTime(ctx sdk.Context, time int64) (val
 	}
 
 	k.cdc.MustUnmarshal(iterator.Value(), &val)
-	_, UpdateTimeSec := types.ParseSpStoragePriceKey(iterator.Key())
-	val.UpdateTimeSec = UpdateTimeSec
+	updateTimeSec := types.ParseSecondarySpStorePriceKey(iterator.Key())
+	val.UpdateTimeSec = updateTimeSec
 	return val, nil
 }
diff --git a/x/sp/keeper/sp_storage_price_test.go b/x/sp/keeper/sp_storage_price_test.go
index 9dfc45ccb..5a548b8bb 100644
--- a/x/sp/keeper/sp_storage_price_test.go
+++ b/x/sp/keeper/sp_storage_price_test.go
@@ -7,23 +7,21 @@ import (
 
 	sdk "github.com/cosmos/cosmos-sdk/types"
 
-	"github.com/bnb-chain/greenfield/testutil/sample"
 	"github.com/bnb-chain/greenfield/x/sp/types"
 )
 
 func (s *KeeperTestSuite) TestGetSpStoragePriceByTime() {
 	ctx := s.ctx.WithBlockTime(time.Unix(100, 0))
-	spAddr := sample.RandAccAddress()
+	spId := uint32(10)
 	spStoragePrice := types.SpStoragePrice{
-		SpAddress:     spAddr.String(),
+		SpId:          spId,
 		UpdateTimeSec: 1,
 		ReadPrice:     sdk.NewDec(100),
 		StorePrice:    sdk.NewDec(100),
 	}
 	s.spKeeper.SetSpStoragePrice(ctx, spStoragePrice)
-	//keeper.SetSpStoragePrice(ctx, spStoragePrice)
 	spStoragePrice2 := types.SpStoragePrice{
-		SpAddress:     spAddr.String(),
+		SpId:          spId,
 		UpdateTimeSec: 100,
 		ReadPrice:     sdk.NewDec(200),
 		StorePrice:    sdk.NewDec(200),
@@ -46,7 +44,7 @@ func (s *KeeperTestSuite) TestGetSpStoragePriceByTime() {
 	}
 	for _, tt := range tests {
 		s.T().Run(tt.name, func(t *testing.T) {
-			gotVal, err := s.spKeeper.GetSpStoragePriceByTime(ctx, spAddr, tt.args.time)
+			gotVal, err := s.spKeeper.GetSpStoragePriceByTime(ctx, spId, tt.args.time)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("GetSpStoragePriceByTime() error = %v, wantErr %v", err, tt.wantErr)
 				return
diff --git a/x/sp/keeper/sp_test.go b/x/sp/keeper/sp_test.go
index 4455be8b0..a92f2d150 100644
--- a/x/sp/keeper/sp_test.go
+++ b/x/sp/keeper/sp_test.go
@@ -16,22 +16,21 @@ import (
 func (s *KeeperTestSuite) TestSetGetStorageProvider() {
 	keeper := s.spKeeper
 	ctx := s.ctx
-	sp := &types.StorageProvider{}
+	sp := &types.StorageProvider{Id: 100}
 	spAccStr := sample.AccAddress()
 	spAcc := sdk.MustAccAddressFromHex(spAccStr)
-
 	sp.OperatorAddress = spAcc.String()
 
 	keeper.SetStorageProvider(ctx, sp)
-	_, found := keeper.GetStorageProvider(ctx, spAcc)
+	_, found := keeper.GetStorageProvider(ctx, 100)
 	if !found {
 		fmt.Printf("no such sp: %s", spAcc)
 	}
 	require.EqualValues(s.T(), found, true)
 }
 
-// TestStorageProviderBasics tests GetStorageProvider, GetStorageProviderByFundingAddr,
-// GetStorageProviderBySealAddr, GetStorageProviderByApprovalAddr
+// TestStorageProviderBasics tests GetStorageProviderByOperatorAddr, GetStorageProviderByFundingAddr,
+// GetStorageProviderBySealAddr, GetStorageProviderByApprovalAddr, GetStorageProviderByBlsKey
 func (s *KeeperTestSuite) TestStorageProviderBasics() {
 	k := s.spKeeper
 	ctx := s.ctx
@@ -47,15 +46,18 @@ func (s *KeeperTestSuite) TestStorageProviderBasics() {
 	approvalAccStr := sample.AccAddress()
 	approvalAcc := sdk.MustAccAddressFromHex(approvalAccStr)
 
+	blsPubKey := sample.RandBlsPubKey()
 	sp := &types.StorageProvider{
+		Id:              100,
 		OperatorAddress: spAcc.String(),
 		FundingAddress:  fundingAcc.String(),
 		SealAddress:     sealAcc.String(),
 		ApprovalAddress: approvalAcc.String(),
+		BlsKey:          blsPubKey,
 	}
 
 	k.SetStorageProvider(ctx, sp)
-	_, found := k.GetStorageProvider(ctx, spAcc)
+	_, found := k.GetStorageProvider(ctx, 100)
 	if !found {
 		fmt.Printf("no such sp: %s", spAcc)
 	}
@@ -81,6 +83,13 @@ func (s *KeeperTestSuite) TestStorageProviderBasics() {
 		fmt.Printf("no such sp: %s", spAcc)
 	}
 	require.EqualValues(s.T(), found, true)
+
+	k.SetStorageProviderByBlsKey(ctx, sp)
+	_, found = k.GetStorageProviderByBlsKey(ctx, blsPubKey)
+	if !found {
+		fmt.Printf("no such sp: %s", spAcc)
+	}
+	require.EqualValues(s.T(), found, true)
 }
 
 func (s *KeeperTestSuite) TestSlashBasic() {
@@ -101,16 +110,20 @@ func (s *KeeperTestSuite) TestSlashBasic() {
 	approvalAccStr := sample.AccAddress()
 	approvalAcc := sdk.MustAccAddressFromHex(approvalAccStr)
 
+	blsPubKey := sample.RandBlsPubKey()
+
 	sp := &types.StorageProvider{
+		Id:              100,
 		OperatorAddress: spAcc.String(),
 		FundingAddress:  fundingAcc.String(),
 		SealAddress:     sealAcc.String(),
 		ApprovalAddress: approvalAcc.String(),
+		BlsKey:          blsPubKey,
 		TotalDeposit:    math.NewIntWithDecimal(2010, types2.DecimalBNB),
 	}
 
 	k.SetStorageProvider(ctx, sp)
-	_, found := k.GetStorageProvider(ctx, spAcc)
+	_, found := k.GetStorageProvider(ctx, 100)
 	if !found {
 		fmt.Printf("no such sp: %s", spAcc)
 	}
@@ -121,10 +134,10 @@ func (s *KeeperTestSuite) TestSlashBasic() {
 		Amount:  sdk.NewCoin(types2.Denom, math.NewIntWithDecimal(10, types2.DecimalBNB)),
 	}
 
-	err := k.Slash(ctx, spAcc, []types.RewardInfo{rewardInfo})
+	err := k.Slash(ctx, sp.Id, []types.RewardInfo{rewardInfo})
 	require.NoError(s.T(), err)
 
-	spAfterSlash, found := k.GetStorageProvider(ctx, spAcc)
+	spAfterSlash, found := k.GetStorageProvider(ctx, 100)
 	require.True(s.T(), found)
 	s.T().Logf("%s", spAfterSlash.TotalDeposit.String())
 	require.True(s.T(), spAfterSlash.TotalDeposit.Equal(math.NewIntWithDecimal(2000, types2.DecimalBNB)))
diff --git a/x/sp/types/errors.go b/x/sp/types/errors.go
index 7a5ddb99b..c7d411c21 100644
--- a/x/sp/types/errors.go
+++ b/x/sp/types/errors.go
@@ -7,7 +7,7 @@ import (
 // x/sp module sentinel errors
 var (
 	ErrStorageProviderNotFound           = errors.Register(ModuleName, 1, "StorageProvider does not exist")
-	ErrStorageProviderNotInService       = errors.Register(ModuleName, 2, "StorageProvider does not exist")
+	ErrStorageProviderNotInService       = errors.Register(ModuleName, 2, "StorageProvider is not in service")
 	ErrStorageProviderOwnerExists        = errors.Register(ModuleName, 3, "StorageProvider already exist for this operator address; must use new StorageProvider operator address")
 	ErrInsufficientDepositAmount         = errors.Register(ModuleName, 4, "insufficient deposit amount")
 	ErrDepositAccountNotAllowed          = errors.Register(ModuleName, 5, "the deposit address must be the fund address of sp.")
@@ -18,6 +18,9 @@ var (
 	ErrStorageProviderGcAddrExists       = errors.Register(ModuleName, 10, "StorageProvider already exist for this gc address; must use new StorageProvider gc address.")
 	ErrStorageProviderPriceExpired       = errors.Register(ModuleName, 11, "StorageProvider price expired")
 	ErrStorageProviderNotChanged         = errors.Register(ModuleName, 12, "StorageProvider not changed")
+	ErrStorageProviderExitFailed         = errors.Register(ModuleName, 13, "StorageProvider exit failed.")
+	ErrStorageProviderInvalidBlsKey      = errors.Register(ModuleName, 14, "StorageProvider bls pubkey is invalid")
+	ErrStorageProviderBlsKeyExists       = errors.Register(ModuleName, 15, "StorageProvider already exist for this bls pubkey; must use new bls pubkey")
 
 	ErrSignerNotGovModule  = errors.Register(ModuleName, 40, "signer is not gov module account")
 	ErrSignerEmpty         = errors.Register(ModuleName, 41, "signer is empty")
diff --git a/x/sp/types/events.pb.go b/x/sp/types/events.pb.go
index 56f520bcb..402332365 100644
--- a/x/sp/types/events.pb.go
+++ b/x/sp/types/events.pb.go
@@ -28,24 +28,28 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
 
 // EventCreateStorageProvider is emitted when there is a storage provider created
 type EventCreateStorageProvider struct {
+	// sp_id defines the identifier of storage provider which generated on-chain
+	SpId uint32 `protobuf:"varint,1,opt,name=sp_id,json=spId,proto3" json:"sp_id,omitempty"`
 	// sp_address is the operator address of the storage provider
-	SpAddress string `protobuf:"bytes,1,opt,name=sp_address,json=spAddress,proto3" json:"sp_address,omitempty"`
+	SpAddress string `protobuf:"bytes,2,opt,name=sp_address,json=spAddress,proto3" json:"sp_address,omitempty"`
 	// funding_address is the funding account address of the storage provider
-	FundingAddress string `protobuf:"bytes,2,opt,name=funding_address,json=fundingAddress,proto3" json:"funding_address,omitempty"`
+	FundingAddress string `protobuf:"bytes,3,opt,name=funding_address,json=fundingAddress,proto3" json:"funding_address,omitempty"`
 	// seal_address is the account address for SealObject Tx
-	SealAddress string `protobuf:"bytes,3,opt,name=seal_address,json=sealAddress,proto3" json:"seal_address,omitempty"`
+	SealAddress string `protobuf:"bytes,4,opt,name=seal_address,json=sealAddress,proto3" json:"seal_address,omitempty"`
 	// approval_address is the account address for approve create bucket/object signature
-	ApprovalAddress string `protobuf:"bytes,4,opt,name=approval_address,json=approvalAddress,proto3" json:"approval_address,omitempty"`
+	ApprovalAddress string `protobuf:"bytes,5,opt,name=approval_address,json=approvalAddress,proto3" json:"approval_address,omitempty"`
 	// gc_address defines one of the storage provider's accounts which is used for gc purpose
-	GcAddress string `protobuf:"bytes,5,opt,name=gc_address,json=gcAddress,proto3" json:"gc_address,omitempty"`
+	GcAddress string `protobuf:"bytes,6,opt,name=gc_address,json=gcAddress,proto3" json:"gc_address,omitempty"`
 	// endpoint is the domain name address used by SP to provide storage services
-	Endpoint string `protobuf:"bytes,6,opt,name=endpoint,proto3" json:"endpoint,omitempty"`
+	Endpoint string `protobuf:"bytes,7,opt,name=endpoint,proto3" json:"endpoint,omitempty"`
 	// total_deposit is the token coin that the storage provider deposit to the storage module
-	TotalDeposit *types.Coin `protobuf:"bytes,7,opt,name=total_deposit,json=totalDeposit,proto3" json:"total_deposit,omitempty"`
+	TotalDeposit *types.Coin `protobuf:"bytes,8,opt,name=total_deposit,json=totalDeposit,proto3" json:"total_deposit,omitempty"`
 	// status defines the status of the storage provider
-	Status Status `protobuf:"varint,8,opt,name=status,proto3,enum=greenfield.sp.Status" json:"status,omitempty"`
+	Status Status `protobuf:"varint,9,opt,name=status,proto3,enum=greenfield.sp.Status" json:"status,omitempty"`
 	// description defines the description terms for the storage provider
-	Description Description `protobuf:"bytes,9,opt,name=description,proto3" json:"description"`
+	Description Description `protobuf:"bytes,10,opt,name=description,proto3" json:"description"`
+	// bls_key defines the bls pub key owned by storage provider used when sealing object and completing migration
+	BlsKey string `protobuf:"bytes,11,opt,name=bls_key,json=blsKey,proto3" json:"bls_key,omitempty"`
 }
 
 func (m *EventCreateStorageProvider) Reset()         { *m = EventCreateStorageProvider{} }
@@ -81,6 +85,13 @@ func (m *EventCreateStorageProvider) XXX_DiscardUnknown() {
 
 var xxx_messageInfo_EventCreateStorageProvider proto.InternalMessageInfo
 
+func (m *EventCreateStorageProvider) GetSpId() uint32 {
+	if m != nil {
+		return m.SpId
+	}
+	return 0
+}
+
 func (m *EventCreateStorageProvider) GetSpAddress() string {
 	if m != nil {
 		return m.SpAddress
@@ -144,20 +155,31 @@ func (m *EventCreateStorageProvider) GetDescription() Description {
 	return Description{}
 }
 
+func (m *EventCreateStorageProvider) GetBlsKey() string {
+	if m != nil {
+		return m.BlsKey
+	}
+	return ""
+}
+
 // EventEditStorageProvider is emitted when SP's metadata is edited.
 type EventEditStorageProvider struct {
+	// sp_id defines the identifier of storage provider which generated on-chain
+	SpId uint32 `protobuf:"varint,1,opt,name=sp_id,json=spId,proto3" json:"sp_id,omitempty"`
 	// sp_address is the operator address of the storage provider
-	SpAddress string `protobuf:"bytes,1,opt,name=sp_address,json=spAddress,proto3" json:"sp_address,omitempty"`
+	SpAddress string `protobuf:"bytes,2,opt,name=sp_address,json=spAddress,proto3" json:"sp_address,omitempty"`
 	// endpoint is the service endpoint of the storage provider
-	Endpoint string `protobuf:"bytes,2,opt,name=endpoint,proto3" json:"endpoint,omitempty"`
+	Endpoint string `protobuf:"bytes,3,opt,name=endpoint,proto3" json:"endpoint,omitempty"`
 	// description defines the description terms for the storage provider
-	Description Description `protobuf:"bytes,3,opt,name=description,proto3" json:"description"`
+	Description Description `protobuf:"bytes,4,opt,name=description,proto3" json:"description"`
 	// seal_address is the account address for SealObject Tx
-	SealAddress string `protobuf:"bytes,4,opt,name=seal_address,json=sealAddress,proto3" json:"seal_address,omitempty"`
+	SealAddress string `protobuf:"bytes,5,opt,name=seal_address,json=sealAddress,proto3" json:"seal_address,omitempty"`
 	// approval_address is the account address for approve create bucket/object signature
-	ApprovalAddress string `protobuf:"bytes,5,opt,name=approval_address,json=approvalAddress,proto3" json:"approval_address,omitempty"`
+	ApprovalAddress string `protobuf:"bytes,6,opt,name=approval_address,json=approvalAddress,proto3" json:"approval_address,omitempty"`
 	// gc_address defines one of the storage provider's accounts which is used for gc purpose
-	GcAddress string `protobuf:"bytes,6,opt,name=gc_address,json=gcAddress,proto3" json:"gc_address,omitempty"`
+	GcAddress string `protobuf:"bytes,7,opt,name=gc_address,json=gcAddress,proto3" json:"gc_address,omitempty"`
+	// bls_key defines the bls pub key owned by storage provider used when sealing object
+	BlsKey string `protobuf:"bytes,8,opt,name=bls_key,json=blsKey,proto3" json:"bls_key,omitempty"`
 }
 
 func (m *EventEditStorageProvider) Reset()         { *m = EventEditStorageProvider{} }
@@ -193,6 +215,13 @@ func (m *EventEditStorageProvider) XXX_DiscardUnknown() {
 
 var xxx_messageInfo_EventEditStorageProvider proto.InternalMessageInfo
 
+func (m *EventEditStorageProvider) GetSpId() uint32 {
+	if m != nil {
+		return m.SpId
+	}
+	return 0
+}
+
 func (m *EventEditStorageProvider) GetSpAddress() string {
 	if m != nil {
 		return m.SpAddress
@@ -235,6 +264,13 @@ func (m *EventEditStorageProvider) GetGcAddress() string {
 	return ""
 }
 
+func (m *EventEditStorageProvider) GetBlsKey() string {
+	if m != nil {
+		return m.BlsKey
+	}
+	return ""
+}
+
 // EventDeposit is emitted when sp deposit tokens.
 type EventDeposit struct {
 	// funding_address is the funding account address of the storage provider
@@ -300,8 +336,8 @@ func (m *EventDeposit) GetTotalDeposit() string {
 }
 
 type EventSpStoragePriceUpdate struct {
-	// sp address
-	SpAddress string `protobuf:"bytes,1,opt,name=sp_address,json=spAddress,proto3" json:"sp_address,omitempty"`
+	// sp id
+	SpId uint32 `protobuf:"varint,1,opt,name=sp_id,json=spId,proto3" json:"sp_id,omitempty"`
 	// update time, in unix timestamp
 	UpdateTimeSec int64 `protobuf:"varint,2,opt,name=update_time_sec,json=updateTimeSec,proto3" json:"update_time_sec,omitempty"`
 	// read price, in bnb wei per charge byte
@@ -345,11 +381,11 @@ func (m *EventSpStoragePriceUpdate) XXX_DiscardUnknown() {
 
 var xxx_messageInfo_EventSpStoragePriceUpdate proto.InternalMessageInfo
 
-func (m *EventSpStoragePriceUpdate) GetSpAddress() string {
+func (m *EventSpStoragePriceUpdate) GetSpId() uint32 {
 	if m != nil {
-		return m.SpAddress
+		return m.SpId
 	}
-	return ""
+	return 0
 }
 
 func (m *EventSpStoragePriceUpdate) GetUpdateTimeSec() int64 {
@@ -424,49 +460,52 @@ func init() {
 func init() { proto.RegisterFile("greenfield/sp/events.proto", fileDescriptor_685cbfa50fdf0841) }
 
 var fileDescriptor_685cbfa50fdf0841 = []byte{
-	// 666 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0xc1, 0x4e, 0x1b, 0x3d,
-	0x10, 0xce, 0x86, 0x10, 0x88, 0x43, 0xe0, 0xd7, 0x8a, 0x5f, 0x5a, 0x72, 0x58, 0xa2, 0x54, 0x42,
-	0xa8, 0x52, 0x76, 0x05, 0x3d, 0x70, 0x68, 0x55, 0x89, 0x10, 0xee, 0xed, 0xa6, 0xbd, 0xb4, 0xaa,
-	0x56, 0xce, 0x7a, 0x58, 0xac, 0x92, 0xb5, 0x6b, 0x3b, 0x51, 0x79, 0x0b, 0x9e, 0xa1, 0xb7, 0xde,
-	0x79, 0x08, 0x8e, 0x88, 0x53, 0xd5, 0x03, 0x42, 0x70, 0xef, 0x33, 0x54, 0xeb, 0xf5, 0x86, 0x24,
-	0x45, 0x4a, 0x0b, 0x39, 0x25, 0x9e, 0x99, 0x6f, 0xe6, 0xdb, 0xcf, 0x9f, 0x6d, 0x54, 0x8f, 0x05,
-	0x40, 0x72, 0x44, 0xe1, 0x84, 0xf8, 0x92, 0xfb, 0x30, 0x84, 0x44, 0x49, 0x8f, 0x0b, 0xa6, 0x98,
-	0x5d, 0xbb, 0xcf, 0x79, 0x92, 0xd7, 0xdd, 0x88, 0xc9, 0x3e, 0x93, 0x7e, 0x0f, 0x4b, 0xf0, 0x87,
-	0x3b, 0x3d, 0x50, 0x78, 0xc7, 0x8f, 0x18, 0x4d, 0xb2, 0xf2, 0xfa, 0x46, 0x96, 0x0f, 0xf5, 0xca,
-	0xcf, 0x16, 0x26, 0xb5, 0x1e, 0xb3, 0x98, 0x65, 0xf1, 0xf4, 0x5f, 0x0e, 0x98, 0x9c, 0xad, 0x4e,
-	0x39, 0x18, 0x40, 0xf3, 0x5b, 0x09, 0xd5, 0x0f, 0x53, 0x2e, 0x07, 0x02, 0xb0, 0x82, 0xae, 0x62,
-	0x02, 0xc7, 0xf0, 0x46, 0xb0, 0x21, 0x25, 0x20, 0xec, 0x3d, 0x84, 0x24, 0x0f, 0x31, 0x21, 0x02,
-	0xa4, 0x74, 0xac, 0x86, 0xb5, 0x5d, 0x69, 0x3b, 0x57, 0xe7, 0xad, 0x75, 0x33, 0x75, 0x3f, 0xcb,
-	0x74, 0x95, 0xa0, 0x49, 0x1c, 0x54, 0x24, 0x37, 0x01, 0x7b, 0x1f, 0xad, 0x1d, 0x0d, 0x12, 0x42,
-	0x93, 0x78, 0x84, 0x2e, 0xce, 0x40, 0xaf, 0x1a, 0x40, 0xde, 0xe2, 0x25, 0x5a, 0x91, 0x80, 0x4f,
-	0x46, 0xf8, 0x85, 0x19, 0xf8, 0x6a, 0x5a, 0x9d, 0x83, 0x0f, 0xd0, 0x7f, 0x98, 0x73, 0xc1, 0x86,
-	0x63, 0x0d, 0x4a, 0x33, 0x1a, 0xac, 0xe5, 0x88, 0xbc, 0xc9, 0x1e, 0x42, 0x71, 0x34, 0x82, 0x2f,
-	0xce, 0xfa, 0xfa, 0x38, 0xca, 0x81, 0x75, 0xb4, 0x0c, 0x09, 0xe1, 0x8c, 0x26, 0xca, 0x29, 0xa7,
-	0xb0, 0x60, 0xb4, 0xb6, 0x5f, 0xa3, 0x9a, 0x62, 0x0a, 0x9f, 0x84, 0x04, 0x38, 0x93, 0x54, 0x39,
-	0x4b, 0x0d, 0x6b, 0xbb, 0xba, 0xbb, 0xe1, 0x99, 0xa6, 0xe9, 0xae, 0x7b, 0x66, 0xd7, 0xbd, 0x03,
-	0x46, 0x93, 0x60, 0x45, 0xd7, 0x77, 0xb2, 0x72, 0xbb, 0x85, 0xca, 0x52, 0x61, 0x35, 0x90, 0xce,
-	0x72, 0xc3, 0xda, 0x5e, 0xdd, 0xfd, 0xdf, 0x9b, 0x70, 0x8f, 0xd7, 0xd5, 0xc9, 0xc0, 0x14, 0xd9,
-	0x6d, 0x54, 0x25, 0x20, 0x23, 0x41, 0xb9, 0xa2, 0x2c, 0x71, 0x2a, 0x7a, 0x58, 0x7d, 0x0a, 0xd3,
-	0xb9, 0xaf, 0x68, 0x97, 0x2e, 0xae, 0x37, 0x0b, 0xc1, 0x38, 0xa8, 0xf9, 0xab, 0x88, 0x1c, 0x6d,
-	0x92, 0x43, 0x42, 0xd5, 0xdc, 0x2c, 0x32, 0x2e, 0x52, 0x71, 0x4a, 0xa4, 0x29, 0xd6, 0x0b, 0x8f,
-	0x60, 0xfd, 0x87, 0x7f, 0x4a, 0x4f, 0xf5, 0xcf, 0xe2, 0xd3, 0xfc, 0x53, 0xfe, 0x6b, 0xff, 0x34,
-	0xcf, 0x2c, 0xb4, 0xa2, 0x05, 0xcf, 0x37, 0xfd, 0x81, 0xe3, 0x64, 0xfd, 0xe3, 0x71, 0x72, 0xd0,
-	0x52, 0xee, 0xb8, 0x4c, 0xed, 0x7c, 0x69, 0x3f, 0x9b, 0x76, 0xa4, 0x3e, 0x69, 0x93, 0xb6, 0x6b,
-	0xde, 0x14, 0xd1, 0x86, 0xa6, 0xd4, 0xe5, 0x23, 0x07, 0xd0, 0x08, 0xde, 0x73, 0x82, 0x15, 0x3c,
-	0xde, 0x04, 0x5b, 0x68, 0x6d, 0xa0, 0x5b, 0x84, 0x8a, 0xf6, 0x21, 0x94, 0x10, 0x69, 0x76, 0x0b,
-	0x41, 0x2d, 0x0b, 0xbf, 0xa3, 0x7d, 0xe8, 0x42, 0x64, 0x7f, 0x44, 0x48, 0x00, 0x26, 0x21, 0x4f,
-	0x87, 0x9a, 0xab, 0xe0, 0x55, 0xba, 0xe7, 0x3f, 0xaf, 0x37, 0xb7, 0x62, 0xaa, 0x8e, 0x07, 0x3d,
-	0x2f, 0x62, 0x7d, 0x73, 0x1b, 0x9a, 0x9f, 0x96, 0x24, 0x9f, 0xcd, 0x6d, 0xd7, 0x81, 0xe8, 0xea,
-	0xbc, 0x85, 0x0c, 0x9d, 0x0e, 0x44, 0x41, 0x25, 0xed, 0xa7, 0xbf, 0x21, 0x25, 0x71, 0x24, 0x00,
-	0x42, 0x3d, 0xe1, 0xcb, 0x80, 0x29, 0xac, 0xcd, 0x52, 0x0a, 0x6a, 0x69, 0x38, 0x00, 0x4c, 0xde,
-	0xa6, 0x41, 0xfb, 0x13, 0xaa, 0x4a, 0xc5, 0x04, 0x18, 0x16, 0x8b, 0x73, 0x60, 0x81, 0x74, 0x43,
-	0x4d, 0xa3, 0xf9, 0xdd, 0x42, 0x8d, 0x4c, 0x62, 0x88, 0x58, 0x42, 0xb0, 0x38, 0xcd, 0xb4, 0x9e,
-	0x50, 0xfa, 0x01, 0xc1, 0xac, 0x87, 0x04, 0x9b, 0xe2, 0x5a, 0x9c, 0x2f, 0xd7, 0x76, 0xe7, 0xe2,
-	0xd6, 0xb5, 0x2e, 0x6f, 0x5d, 0xeb, 0xe6, 0xd6, 0xb5, 0xce, 0xee, 0xdc, 0xc2, 0xe5, 0x9d, 0x5b,
-	0xf8, 0x71, 0xe7, 0x16, 0x3e, 0x3c, 0x1f, 0xeb, 0xdd, 0x4b, 0x7a, 0xad, 0xe8, 0x18, 0xd3, 0xc4,
-	0x1f, 0x7b, 0x81, 0xbe, 0x8e, 0xde, 0xa0, 0x5e, 0x59, 0x3f, 0x42, 0x2f, 0x7e, 0x07, 0x00, 0x00,
-	0xff, 0xff, 0xd2, 0x8f, 0x72, 0x18, 0x1d, 0x07, 0x00, 0x00,
+	// 711 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0xcd, 0x4e, 0xdb, 0x4a,
+	0x14, 0x8e, 0xc9, 0x1f, 0x99, 0x10, 0xb8, 0xf2, 0xe5, 0xea, 0x9a, 0x2c, 0x42, 0x94, 0x4a, 0x28,
+	0xaa, 0x14, 0x47, 0xd0, 0x05, 0x8b, 0x56, 0x95, 0x08, 0x61, 0x51, 0x75, 0xd3, 0x3a, 0xed, 0xa6,
+	0x55, 0x65, 0x8d, 0x3d, 0x07, 0x33, 0x22, 0xf1, 0x4c, 0x3d, 0x93, 0xa8, 0x79, 0x0b, 0x1e, 0xa0,
+	0x2f, 0xd0, 0x3d, 0xcb, 0x3e, 0x00, 0x4b, 0xc4, 0xaa, 0xea, 0x02, 0x55, 0xf0, 0x22, 0x95, 0xc7,
+	0xe3, 0x90, 0x44, 0x91, 0xd2, 0x16, 0xba, 0x4a, 0xe6, 0x9c, 0xf3, 0x7d, 0xf3, 0xcd, 0x39, 0x9f,
+	0x67, 0x50, 0x35, 0x88, 0x00, 0xc2, 0x63, 0x0a, 0x7d, 0xd2, 0x16, 0xbc, 0x0d, 0x23, 0x08, 0xa5,
+	0xb0, 0x79, 0xc4, 0x24, 0x33, 0x2b, 0x77, 0x39, 0x5b, 0xf0, 0x6a, 0xcd, 0x67, 0x62, 0xc0, 0x44,
+	0xdb, 0xc3, 0x02, 0xda, 0xa3, 0x5d, 0x0f, 0x24, 0xde, 0x6d, 0xfb, 0x8c, 0x86, 0x49, 0x79, 0x75,
+	0x2b, 0xc9, 0xbb, 0x6a, 0xd5, 0x4e, 0x16, 0x3a, 0xb5, 0x19, 0xb0, 0x80, 0x25, 0xf1, 0xf8, 0x5f,
+	0x0a, 0x98, 0xdd, 0x5b, 0x8e, 0x39, 0x68, 0x40, 0xe3, 0x32, 0x87, 0xaa, 0x47, 0xb1, 0x96, 0xc3,
+	0x08, 0xb0, 0x84, 0x9e, 0x64, 0x11, 0x0e, 0xe0, 0x55, 0xc4, 0x46, 0x94, 0x40, 0x64, 0xfe, 0x8b,
+	0xf2, 0x82, 0xbb, 0x94, 0x58, 0x46, 0xdd, 0x68, 0x56, 0x9c, 0x9c, 0xe0, 0x2f, 0x88, 0xb9, 0x8f,
+	0x90, 0xe0, 0x2e, 0x26, 0x24, 0x02, 0x21, 0xac, 0x95, 0xba, 0xd1, 0x2c, 0x75, 0xac, 0xab, 0xf3,
+	0xd6, 0xa6, 0x96, 0x72, 0x90, 0x64, 0x7a, 0x32, 0xa2, 0x61, 0xe0, 0x94, 0x04, 0xd7, 0x01, 0xf3,
+	0x00, 0x6d, 0x1c, 0x0f, 0x43, 0x42, 0xc3, 0x60, 0x82, 0xce, 0x2e, 0x41, 0xaf, 0x6b, 0x40, 0x4a,
+	0xf1, 0x14, 0xad, 0x09, 0xc0, 0xfd, 0x09, 0x3e, 0xb7, 0x04, 0x5f, 0x8e, 0xab, 0x53, 0xf0, 0x21,
+	0xfa, 0x07, 0x73, 0x1e, 0xb1, 0xd1, 0x14, 0x41, 0x7e, 0x09, 0xc1, 0x46, 0x8a, 0x48, 0x49, 0xf6,
+	0x11, 0x0a, 0xfc, 0x09, 0xbc, 0xb0, 0xec, 0xf4, 0x81, 0x9f, 0x02, 0xab, 0x68, 0x15, 0x42, 0xc2,
+	0x19, 0x0d, 0xa5, 0x55, 0x8c, 0x61, 0xce, 0x64, 0x6d, 0x3e, 0x47, 0x15, 0xc9, 0x24, 0xee, 0xbb,
+	0x04, 0x38, 0x13, 0x54, 0x5a, 0xab, 0x75, 0xa3, 0x59, 0xde, 0xdb, 0xb2, 0x35, 0x69, 0x6c, 0x05,
+	0x5b, 0x5b, 0xc1, 0x3e, 0x64, 0x34, 0x74, 0xd6, 0x54, 0x7d, 0x37, 0x29, 0x37, 0x5b, 0xa8, 0x20,
+	0x24, 0x96, 0x43, 0x61, 0x95, 0xea, 0x46, 0x73, 0x7d, 0xef, 0x3f, 0x7b, 0xc6, 0x52, 0x76, 0x4f,
+	0x25, 0x1d, 0x5d, 0x64, 0x76, 0x50, 0x99, 0x80, 0xf0, 0x23, 0xca, 0x25, 0x65, 0xa1, 0x85, 0xd4,
+	0x66, 0xd5, 0x39, 0x4c, 0xf7, 0xae, 0xa2, 0x93, 0xbb, 0xb8, 0xde, 0xce, 0x38, 0xd3, 0x20, 0xf3,
+	0x7f, 0x54, 0xf4, 0xfa, 0xc2, 0x3d, 0x85, 0xb1, 0x55, 0x56, 0xa7, 0x29, 0x78, 0x7d, 0xf1, 0x12,
+	0xc6, 0x8d, 0xcf, 0x59, 0x64, 0x29, 0x4b, 0x1d, 0x11, 0x2a, 0xff, 0xae, 0xa1, 0xa6, 0x5b, 0x9a,
+	0x9d, 0x6b, 0xe9, 0xdc, 0x19, 0x73, 0x7f, 0x72, 0xc6, 0x79, 0xb7, 0xe5, 0xef, 0xeb, 0xb6, 0xc2,
+	0xfd, 0xdc, 0x56, 0xfc, 0x75, 0xb7, 0x4d, 0x8d, 0x67, 0x75, 0x66, 0x3c, 0x67, 0x06, 0x5a, 0x53,
+	0xe3, 0x49, 0xbd, 0xb3, 0xe0, 0xab, 0x34, 0x7e, 0xf3, 0xab, 0xb4, 0x50, 0x31, 0x35, 0xae, 0x9a,
+	0x9e, 0x93, 0x2e, 0xcd, 0x47, 0xf3, 0xc6, 0x4e, 0xc6, 0x34, 0xe3, 0xde, 0xc6, 0xd7, 0x15, 0xb4,
+	0xa5, 0x24, 0xf5, 0xf8, 0xc4, 0x2f, 0xd4, 0x87, 0xb7, 0x9c, 0x60, 0x09, 0x8b, 0x2d, 0xb3, 0x83,
+	0x36, 0x86, 0x2a, 0xed, 0x4a, 0x3a, 0x00, 0x57, 0x80, 0xaf, 0x76, 0xce, 0x3a, 0x95, 0x24, 0xfc,
+	0x86, 0x0e, 0xa0, 0x07, 0xbe, 0xf9, 0x1e, 0xa1, 0x08, 0x30, 0x71, 0x79, 0x4c, 0xa8, 0x6f, 0x9b,
+	0x67, 0xf1, 0xa0, 0xbf, 0x5f, 0x6f, 0xef, 0x04, 0x54, 0x9e, 0x0c, 0x3d, 0xdb, 0x67, 0x03, 0x7d,
+	0x8b, 0xea, 0x9f, 0x96, 0x20, 0xa7, 0xfa, 0x96, 0xec, 0x82, 0x7f, 0x75, 0xde, 0x42, 0xba, 0x0b,
+	0x5d, 0xf0, 0x9d, 0x52, 0xcc, 0xa7, 0xf4, 0xc5, 0x22, 0x8e, 0x23, 0x00, 0x57, 0xed, 0xf0, 0x71,
+	0xc8, 0x24, 0x56, 0x36, 0xcb, 0x39, 0x95, 0x38, 0xec, 0x00, 0x26, 0xaf, 0xe3, 0xa0, 0xf9, 0x01,
+	0x95, 0x85, 0x64, 0x11, 0x68, 0x15, 0xf9, 0x07, 0x50, 0x81, 0x14, 0xa1, 0x92, 0xd1, 0xf8, 0x62,
+	0xa0, 0x7a, 0xd2, 0x3e, 0xf0, 0x59, 0x48, 0x70, 0x34, 0x4e, 0xfa, 0x38, 0xd3, 0xc5, 0x05, 0x0d,
+	0x33, 0x16, 0x35, 0x6c, 0x4e, 0xeb, 0xca, 0xc3, 0x6a, 0xed, 0x74, 0x2f, 0x6e, 0x6a, 0xc6, 0xe5,
+	0x4d, 0xcd, 0xf8, 0x71, 0x53, 0x33, 0xce, 0x6e, 0x6b, 0x99, 0xcb, 0xdb, 0x5a, 0xe6, 0xdb, 0x6d,
+	0x2d, 0xf3, 0xee, 0xf1, 0x14, 0xb7, 0x17, 0x7a, 0x2d, 0xff, 0x04, 0xd3, 0xb0, 0x3d, 0xf5, 0x72,
+	0x7d, 0x9a, 0xbc, 0x5d, 0x5e, 0x41, 0x3d, 0x5e, 0x4f, 0x7e, 0x06, 0x00, 0x00, 0xff, 0xff, 0xa7,
+	0xee, 0x58, 0xf0, 0x55, 0x07, 0x00, 0x00,
 }
 
 func (m *EventCreateStorageProvider) Marshal() (dAtA []byte, err error) {
@@ -489,6 +528,13 @@ func (m *EventCreateStorageProvider) MarshalToSizedBuffer(dAtA []byte) (int, err
 	_ = i
 	var l int
 	_ = l
+	if len(m.BlsKey) > 0 {
+		i -= len(m.BlsKey)
+		copy(dAtA[i:], m.BlsKey)
+		i = encodeVarintEvents(dAtA, i, uint64(len(m.BlsKey)))
+		i--
+		dAtA[i] = 0x5a
+	}
 	{
 		size, err := m.Description.MarshalToSizedBuffer(dAtA[:i])
 		if err != nil {
@@ -498,11 +544,11 @@ func (m *EventCreateStorageProvider) MarshalToSizedBuffer(dAtA []byte) (int, err
 		i = encodeVarintEvents(dAtA, i, uint64(size))
 	}
 	i--
-	dAtA[i] = 0x4a
+	dAtA[i] = 0x52
 	if m.Status != 0 {
 		i = encodeVarintEvents(dAtA, i, uint64(m.Status))
 		i--
-		dAtA[i] = 0x40
+		dAtA[i] = 0x48
 	}
 	if m.TotalDeposit != nil {
 		{
@@ -514,49 +560,54 @@ func (m *EventCreateStorageProvider) MarshalToSizedBuffer(dAtA []byte) (int, err
 			i = encodeVarintEvents(dAtA, i, uint64(size))
 		}
 		i--
-		dAtA[i] = 0x3a
+		dAtA[i] = 0x42
 	}
 	if len(m.Endpoint) > 0 {
 		i -= len(m.Endpoint)
 		copy(dAtA[i:], m.Endpoint)
 		i = encodeVarintEvents(dAtA, i, uint64(len(m.Endpoint)))
 		i--
-		dAtA[i] = 0x32
+		dAtA[i] = 0x3a
 	}
 	if len(m.GcAddress) > 0 {
 		i -= len(m.GcAddress)
 		copy(dAtA[i:], m.GcAddress)
 		i = encodeVarintEvents(dAtA, i, uint64(len(m.GcAddress)))
 		i--
-		dAtA[i] = 0x2a
+		dAtA[i] = 0x32
 	}
 	if len(m.ApprovalAddress) > 0 {
 		i -= len(m.ApprovalAddress)
 		copy(dAtA[i:], m.ApprovalAddress)
 		i = encodeVarintEvents(dAtA, i, uint64(len(m.ApprovalAddress)))
 		i--
-		dAtA[i] = 0x22
+		dAtA[i] = 0x2a
 	}
 	if len(m.SealAddress) > 0 {
 		i -= len(m.SealAddress)
 		copy(dAtA[i:], m.SealAddress)
 		i = encodeVarintEvents(dAtA, i, uint64(len(m.SealAddress)))
 		i--
-		dAtA[i] = 0x1a
+		dAtA[i] = 0x22
 	}
 	if len(m.FundingAddress) > 0 {
 		i -= len(m.FundingAddress)
 		copy(dAtA[i:], m.FundingAddress)
 		i = encodeVarintEvents(dAtA, i, uint64(len(m.FundingAddress)))
 		i--
-		dAtA[i] = 0x12
+		dAtA[i] = 0x1a
 	}
 	if len(m.SpAddress) > 0 {
 		i -= len(m.SpAddress)
 		copy(dAtA[i:], m.SpAddress)
 		i = encodeVarintEvents(dAtA, i, uint64(len(m.SpAddress)))
 		i--
-		dAtA[i] = 0xa
+		dAtA[i] = 0x12
+	}
+	if m.SpId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.SpId))
+		i--
+		dAtA[i] = 0x8
 	}
 	return len(dAtA) - i, nil
 }
@@ -581,26 +632,33 @@ func (m *EventEditStorageProvider) MarshalToSizedBuffer(dAtA []byte) (int, error
 	_ = i
 	var l int
 	_ = l
+	if len(m.BlsKey) > 0 {
+		i -= len(m.BlsKey)
+		copy(dAtA[i:], m.BlsKey)
+		i = encodeVarintEvents(dAtA, i, uint64(len(m.BlsKey)))
+		i--
+		dAtA[i] = 0x42
+	}
 	if len(m.GcAddress) > 0 {
 		i -= len(m.GcAddress)
 		copy(dAtA[i:], m.GcAddress)
 		i = encodeVarintEvents(dAtA, i, uint64(len(m.GcAddress)))
 		i--
-		dAtA[i] = 0x32
+		dAtA[i] = 0x3a
 	}
 	if len(m.ApprovalAddress) > 0 {
 		i -= len(m.ApprovalAddress)
 		copy(dAtA[i:], m.ApprovalAddress)
 		i = encodeVarintEvents(dAtA, i, uint64(len(m.ApprovalAddress)))
 		i--
-		dAtA[i] = 0x2a
+		dAtA[i] = 0x32
 	}
 	if len(m.SealAddress) > 0 {
 		i -= len(m.SealAddress)
 		copy(dAtA[i:], m.SealAddress)
 		i = encodeVarintEvents(dAtA, i, uint64(len(m.SealAddress)))
 		i--
-		dAtA[i] = 0x22
+		dAtA[i] = 0x2a
 	}
 	{
 		size, err := m.Description.MarshalToSizedBuffer(dAtA[:i])
@@ -611,20 +669,25 @@ func (m *EventEditStorageProvider) MarshalToSizedBuffer(dAtA []byte) (int, error
 		i = encodeVarintEvents(dAtA, i, uint64(size))
 	}
 	i--
-	dAtA[i] = 0x1a
+	dAtA[i] = 0x22
 	if len(m.Endpoint) > 0 {
 		i -= len(m.Endpoint)
 		copy(dAtA[i:], m.Endpoint)
 		i = encodeVarintEvents(dAtA, i, uint64(len(m.Endpoint)))
 		i--
-		dAtA[i] = 0x12
+		dAtA[i] = 0x1a
 	}
 	if len(m.SpAddress) > 0 {
 		i -= len(m.SpAddress)
 		copy(dAtA[i:], m.SpAddress)
 		i = encodeVarintEvents(dAtA, i, uint64(len(m.SpAddress)))
 		i--
-		dAtA[i] = 0xa
+		dAtA[i] = 0x12
+	}
+	if m.SpId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.SpId))
+		i--
+		dAtA[i] = 0x8
 	}
 	return len(dAtA) - i, nil
 }
@@ -723,12 +786,10 @@ func (m *EventSpStoragePriceUpdate) MarshalToSizedBuffer(dAtA []byte) (int, erro
 		i--
 		dAtA[i] = 0x10
 	}
-	if len(m.SpAddress) > 0 {
-		i -= len(m.SpAddress)
-		copy(dAtA[i:], m.SpAddress)
-		i = encodeVarintEvents(dAtA, i, uint64(len(m.SpAddress)))
+	if m.SpId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.SpId))
 		i--
-		dAtA[i] = 0xa
+		dAtA[i] = 0x8
 	}
 	return len(dAtA) - i, nil
 }
@@ -788,6 +849,9 @@ func (m *EventCreateStorageProvider) Size() (n int) {
 	}
 	var l int
 	_ = l
+	if m.SpId != 0 {
+		n += 1 + sovEvents(uint64(m.SpId))
+	}
 	l = len(m.SpAddress)
 	if l > 0 {
 		n += 1 + l + sovEvents(uint64(l))
@@ -821,6 +885,10 @@ func (m *EventCreateStorageProvider) Size() (n int) {
 	}
 	l = m.Description.Size()
 	n += 1 + l + sovEvents(uint64(l))
+	l = len(m.BlsKey)
+	if l > 0 {
+		n += 1 + l + sovEvents(uint64(l))
+	}
 	return n
 }
 
@@ -830,6 +898,9 @@ func (m *EventEditStorageProvider) Size() (n int) {
 	}
 	var l int
 	_ = l
+	if m.SpId != 0 {
+		n += 1 + sovEvents(uint64(m.SpId))
+	}
 	l = len(m.SpAddress)
 	if l > 0 {
 		n += 1 + l + sovEvents(uint64(l))
@@ -852,6 +923,10 @@ func (m *EventEditStorageProvider) Size() (n int) {
 	if l > 0 {
 		n += 1 + l + sovEvents(uint64(l))
 	}
+	l = len(m.BlsKey)
+	if l > 0 {
+		n += 1 + l + sovEvents(uint64(l))
+	}
 	return n
 }
 
@@ -882,9 +957,8 @@ func (m *EventSpStoragePriceUpdate) Size() (n int) {
 	}
 	var l int
 	_ = l
-	l = len(m.SpAddress)
-	if l > 0 {
-		n += 1 + l + sovEvents(uint64(l))
+	if m.SpId != 0 {
+		n += 1 + sovEvents(uint64(m.SpId))
 	}
 	if m.UpdateTimeSec != 0 {
 		n += 1 + sovEvents(uint64(m.UpdateTimeSec))
@@ -949,6 +1023,25 @@ func (m *EventCreateStorageProvider) Unmarshal(dAtA []byte) error {
 		}
 		switch fieldNum {
 		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field SpId", wireType)
+			}
+			m.SpId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.SpId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field SpAddress", wireType)
 			}
@@ -980,7 +1073,7 @@ func (m *EventCreateStorageProvider) Unmarshal(dAtA []byte) error {
 			}
 			m.SpAddress = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
-		case 2:
+		case 3:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field FundingAddress", wireType)
 			}
@@ -1012,7 +1105,7 @@ func (m *EventCreateStorageProvider) Unmarshal(dAtA []byte) error {
 			}
 			m.FundingAddress = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
-		case 3:
+		case 4:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field SealAddress", wireType)
 			}
@@ -1044,7 +1137,7 @@ func (m *EventCreateStorageProvider) Unmarshal(dAtA []byte) error {
 			}
 			m.SealAddress = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
-		case 4:
+		case 5:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field ApprovalAddress", wireType)
 			}
@@ -1076,7 +1169,7 @@ func (m *EventCreateStorageProvider) Unmarshal(dAtA []byte) error {
 			}
 			m.ApprovalAddress = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
-		case 5:
+		case 6:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field GcAddress", wireType)
 			}
@@ -1108,7 +1201,7 @@ func (m *EventCreateStorageProvider) Unmarshal(dAtA []byte) error {
 			}
 			m.GcAddress = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
-		case 6:
+		case 7:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field Endpoint", wireType)
 			}
@@ -1140,7 +1233,7 @@ func (m *EventCreateStorageProvider) Unmarshal(dAtA []byte) error {
 			}
 			m.Endpoint = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
-		case 7:
+		case 8:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field TotalDeposit", wireType)
 			}
@@ -1176,7 +1269,7 @@ func (m *EventCreateStorageProvider) Unmarshal(dAtA []byte) error {
 				return err
 			}
 			iNdEx = postIndex
-		case 8:
+		case 9:
 			if wireType != 0 {
 				return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType)
 			}
@@ -1195,7 +1288,7 @@ func (m *EventCreateStorageProvider) Unmarshal(dAtA []byte) error {
 					break
 				}
 			}
-		case 9:
+		case 10:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType)
 			}
@@ -1228,6 +1321,38 @@ func (m *EventCreateStorageProvider) Unmarshal(dAtA []byte) error {
 				return err
 			}
 			iNdEx = postIndex
+		case 11:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field BlsKey", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthEvents
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.BlsKey = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipEvents(dAtA[iNdEx:])
@@ -1279,6 +1404,25 @@ func (m *EventEditStorageProvider) Unmarshal(dAtA []byte) error {
 		}
 		switch fieldNum {
 		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field SpId", wireType)
+			}
+			m.SpId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.SpId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field SpAddress", wireType)
 			}
@@ -1310,7 +1454,7 @@ func (m *EventEditStorageProvider) Unmarshal(dAtA []byte) error {
 			}
 			m.SpAddress = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
-		case 2:
+		case 3:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field Endpoint", wireType)
 			}
@@ -1342,7 +1486,7 @@ func (m *EventEditStorageProvider) Unmarshal(dAtA []byte) error {
 			}
 			m.Endpoint = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
-		case 3:
+		case 4:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType)
 			}
@@ -1375,7 +1519,7 @@ func (m *EventEditStorageProvider) Unmarshal(dAtA []byte) error {
 				return err
 			}
 			iNdEx = postIndex
-		case 4:
+		case 5:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field SealAddress", wireType)
 			}
@@ -1407,7 +1551,7 @@ func (m *EventEditStorageProvider) Unmarshal(dAtA []byte) error {
 			}
 			m.SealAddress = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
-		case 5:
+		case 6:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field ApprovalAddress", wireType)
 			}
@@ -1439,7 +1583,7 @@ func (m *EventEditStorageProvider) Unmarshal(dAtA []byte) error {
 			}
 			m.ApprovalAddress = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
-		case 6:
+		case 7:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field GcAddress", wireType)
 			}
@@ -1471,6 +1615,38 @@ func (m *EventEditStorageProvider) Unmarshal(dAtA []byte) error {
 			}
 			m.GcAddress = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
+		case 8:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field BlsKey", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthEvents
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.BlsKey = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipEvents(dAtA[iNdEx:])
@@ -1668,10 +1844,10 @@ func (m *EventSpStoragePriceUpdate) Unmarshal(dAtA []byte) error {
 		}
 		switch fieldNum {
 		case 1:
-			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field SpAddress", wireType)
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field SpId", wireType)
 			}
-			var stringLen uint64
+			m.SpId = 0
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowEvents
@@ -1681,24 +1857,11 @@ func (m *EventSpStoragePriceUpdate) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				stringLen |= uint64(b&0x7F) << shift
+				m.SpId |= uint32(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
-			intStringLen := int(stringLen)
-			if intStringLen < 0 {
-				return ErrInvalidLengthEvents
-			}
-			postIndex := iNdEx + intStringLen
-			if postIndex < 0 {
-				return ErrInvalidLengthEvents
-			}
-			if postIndex > l {
-				return io.ErrUnexpectedEOF
-			}
-			m.SpAddress = string(dAtA[iNdEx:postIndex])
-			iNdEx = postIndex
 		case 2:
 			if wireType != 0 {
 				return fmt.Errorf("proto: wrong wireType = %d for field UpdateTimeSec", wireType)
diff --git a/x/sp/types/expected_keepers_mocks.go b/x/sp/types/expected_keepers_mocks.go
index 88965c015..a05c466e8 100644
--- a/x/sp/types/expected_keepers_mocks.go
+++ b/x/sp/types/expected_keepers_mocks.go
@@ -1,7 +1,7 @@
 // Code generated by MockGen. DO NOT EDIT.
-// Source: github.com/bnb-chain/greenfield/x/sp/types (interfaces: AccountKeeper,BankKeeper,AuthzKeeper)
+// Source: x/sp/types/expected_keepers.go
 
-// Package mock_types is a generated GoMock package.
+// Package types is a generated GoMock package.
 package types
 
 import (
@@ -13,264 +13,264 @@ import (
 	gomock "github.com/golang/mock/gomock"
 )
 
-// MockAccountKeeper is a mock of AccountKeeper interface
+// MockAccountKeeper is a mock of AccountKeeper interface.
 type MockAccountKeeper struct {
 	ctrl     *gomock.Controller
 	recorder *MockAccountKeeperMockRecorder
 }
 
-// MockAccountKeeperMockRecorder is the mock recorder for MockAccountKeeper
+// MockAccountKeeperMockRecorder is the mock recorder for MockAccountKeeper.
 type MockAccountKeeperMockRecorder struct {
 	mock *MockAccountKeeper
 }
 
-// NewMockAccountKeeper creates a new mock instance
+// NewMockAccountKeeper creates a new mock instance.
 func NewMockAccountKeeper(ctrl *gomock.Controller) *MockAccountKeeper {
 	mock := &MockAccountKeeper{ctrl: ctrl}
 	mock.recorder = &MockAccountKeeperMockRecorder{mock}
 	return mock
 }
 
-// EXPECT returns an object that allows the caller to indicate expected use
+// EXPECT returns an object that allows the caller to indicate expected use.
 func (m *MockAccountKeeper) EXPECT() *MockAccountKeeperMockRecorder {
 	return m.recorder
 }
 
-// GetAccount mocks base method
-func (m *MockAccountKeeper) GetAccount(arg0 types.Context, arg1 types.AccAddress) types0.AccountI {
+// GetAccount mocks base method.
+func (m *MockAccountKeeper) GetAccount(ctx types.Context, addr types.AccAddress) types0.AccountI {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetAccount", arg0, arg1)
+	ret := m.ctrl.Call(m, "GetAccount", ctx, addr)
 	ret0, _ := ret[0].(types0.AccountI)
 	return ret0
 }
 
-// GetAccount indicates an expected call of GetAccount
-func (mr *MockAccountKeeperMockRecorder) GetAccount(arg0, arg1 interface{}) *gomock.Call {
+// GetAccount indicates an expected call of GetAccount.
+func (mr *MockAccountKeeperMockRecorder) GetAccount(ctx, addr interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetAccount), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetAccount), ctx, addr)
 }
 
-// GetModuleAccount mocks base method
-func (m *MockAccountKeeper) GetModuleAccount(arg0 types.Context, arg1 string) types0.ModuleAccountI {
+// GetModuleAccount mocks base method.
+func (m *MockAccountKeeper) GetModuleAccount(ctx types.Context, moduleName string) types0.ModuleAccountI {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetModuleAccount", arg0, arg1)
+	ret := m.ctrl.Call(m, "GetModuleAccount", ctx, moduleName)
 	ret0, _ := ret[0].(types0.ModuleAccountI)
 	return ret0
 }
 
-// GetModuleAccount indicates an expected call of GetModuleAccount
-func (mr *MockAccountKeeperMockRecorder) GetModuleAccount(arg0, arg1 interface{}) *gomock.Call {
+// GetModuleAccount indicates an expected call of GetModuleAccount.
+func (mr *MockAccountKeeperMockRecorder) GetModuleAccount(ctx, moduleName interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetModuleAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetModuleAccount), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetModuleAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetModuleAccount), ctx, moduleName)
 }
 
-// GetModuleAddress mocks base method
-func (m *MockAccountKeeper) GetModuleAddress(arg0 string) types.AccAddress {
+// GetModuleAddress mocks base method.
+func (m *MockAccountKeeper) GetModuleAddress(name string) types.AccAddress {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetModuleAddress", arg0)
+	ret := m.ctrl.Call(m, "GetModuleAddress", name)
 	ret0, _ := ret[0].(types.AccAddress)
 	return ret0
 }
 
-// GetModuleAddress indicates an expected call of GetModuleAddress
-func (mr *MockAccountKeeperMockRecorder) GetModuleAddress(arg0 interface{}) *gomock.Call {
+// GetModuleAddress indicates an expected call of GetModuleAddress.
+func (mr *MockAccountKeeperMockRecorder) GetModuleAddress(name interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetModuleAddress", reflect.TypeOf((*MockAccountKeeper)(nil).GetModuleAddress), arg0)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetModuleAddress", reflect.TypeOf((*MockAccountKeeper)(nil).GetModuleAddress), name)
 }
 
-// IterateAccounts mocks base method
-func (m *MockAccountKeeper) IterateAccounts(arg0 types.Context, arg1 func(types0.AccountI) bool) {
+// IterateAccounts mocks base method.
+func (m *MockAccountKeeper) IterateAccounts(ctx types.Context, process func(types0.AccountI) bool) {
 	m.ctrl.T.Helper()
-	m.ctrl.Call(m, "IterateAccounts", arg0, arg1)
+	m.ctrl.Call(m, "IterateAccounts", ctx, process)
 }
 
-// IterateAccounts indicates an expected call of IterateAccounts
-func (mr *MockAccountKeeperMockRecorder) IterateAccounts(arg0, arg1 interface{}) *gomock.Call {
+// IterateAccounts indicates an expected call of IterateAccounts.
+func (mr *MockAccountKeeperMockRecorder) IterateAccounts(ctx, process interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IterateAccounts", reflect.TypeOf((*MockAccountKeeper)(nil).IterateAccounts), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IterateAccounts", reflect.TypeOf((*MockAccountKeeper)(nil).IterateAccounts), ctx, process)
 }
 
-// SetModuleAccount mocks base method
+// SetModuleAccount mocks base method.
 func (m *MockAccountKeeper) SetModuleAccount(arg0 types.Context, arg1 types0.ModuleAccountI) {
 	m.ctrl.T.Helper()
 	m.ctrl.Call(m, "SetModuleAccount", arg0, arg1)
 }
 
-// SetModuleAccount indicates an expected call of SetModuleAccount
+// SetModuleAccount indicates an expected call of SetModuleAccount.
 func (mr *MockAccountKeeperMockRecorder) SetModuleAccount(arg0, arg1 interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetModuleAccount", reflect.TypeOf((*MockAccountKeeper)(nil).SetModuleAccount), arg0, arg1)
 }
 
-// MockBankKeeper is a mock of BankKeeper interface
+// MockBankKeeper is a mock of BankKeeper interface.
 type MockBankKeeper struct {
 	ctrl     *gomock.Controller
 	recorder *MockBankKeeperMockRecorder
 }
 
-// MockBankKeeperMockRecorder is the mock recorder for MockBankKeeper
+// MockBankKeeperMockRecorder is the mock recorder for MockBankKeeper.
 type MockBankKeeperMockRecorder struct {
 	mock *MockBankKeeper
 }
 
-// NewMockBankKeeper creates a new mock instance
+// NewMockBankKeeper creates a new mock instance.
 func NewMockBankKeeper(ctrl *gomock.Controller) *MockBankKeeper {
 	mock := &MockBankKeeper{ctrl: ctrl}
 	mock.recorder = &MockBankKeeperMockRecorder{mock}
 	return mock
 }
 
-// EXPECT returns an object that allows the caller to indicate expected use
+// EXPECT returns an object that allows the caller to indicate expected use.
 func (m *MockBankKeeper) EXPECT() *MockBankKeeperMockRecorder {
 	return m.recorder
 }
 
-// GetAllBalances mocks base method
-func (m *MockBankKeeper) GetAllBalances(arg0 types.Context, arg1 types.AccAddress) types.Coins {
+// GetAllBalances mocks base method.
+func (m *MockBankKeeper) GetAllBalances(ctx types.Context, addr types.AccAddress) types.Coins {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetAllBalances", arg0, arg1)
+	ret := m.ctrl.Call(m, "GetAllBalances", ctx, addr)
 	ret0, _ := ret[0].(types.Coins)
 	return ret0
 }
 
-// GetAllBalances indicates an expected call of GetAllBalances
-func (mr *MockBankKeeperMockRecorder) GetAllBalances(arg0, arg1 interface{}) *gomock.Call {
+// GetAllBalances indicates an expected call of GetAllBalances.
+func (mr *MockBankKeeperMockRecorder) GetAllBalances(ctx, addr interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllBalances", reflect.TypeOf((*MockBankKeeper)(nil).GetAllBalances), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllBalances", reflect.TypeOf((*MockBankKeeper)(nil).GetAllBalances), ctx, addr)
 }
 
-// GetBalance mocks base method
-func (m *MockBankKeeper) GetBalance(arg0 types.Context, arg1 types.AccAddress, arg2 string) types.Coin {
+// GetBalance mocks base method.
+func (m *MockBankKeeper) GetBalance(ctx types.Context, addr types.AccAddress, denom string) types.Coin {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetBalance", arg0, arg1, arg2)
+	ret := m.ctrl.Call(m, "GetBalance", ctx, addr, denom)
 	ret0, _ := ret[0].(types.Coin)
 	return ret0
 }
 
-// GetBalance indicates an expected call of GetBalance
-func (mr *MockBankKeeperMockRecorder) GetBalance(arg0, arg1, arg2 interface{}) *gomock.Call {
+// GetBalance indicates an expected call of GetBalance.
+func (mr *MockBankKeeperMockRecorder) GetBalance(ctx, addr, denom interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBalance", reflect.TypeOf((*MockBankKeeper)(nil).GetBalance), arg0, arg1, arg2)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBalance", reflect.TypeOf((*MockBankKeeper)(nil).GetBalance), ctx, addr, denom)
 }
 
-// LockedCoins mocks base method
-func (m *MockBankKeeper) LockedCoins(arg0 types.Context, arg1 types.AccAddress) types.Coins {
+// LockedCoins mocks base method.
+func (m *MockBankKeeper) LockedCoins(ctx types.Context, addr types.AccAddress) types.Coins {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "LockedCoins", arg0, arg1)
+	ret := m.ctrl.Call(m, "LockedCoins", ctx, addr)
 	ret0, _ := ret[0].(types.Coins)
 	return ret0
 }
 
-// LockedCoins indicates an expected call of LockedCoins
-func (mr *MockBankKeeperMockRecorder) LockedCoins(arg0, arg1 interface{}) *gomock.Call {
+// LockedCoins indicates an expected call of LockedCoins.
+func (mr *MockBankKeeperMockRecorder) LockedCoins(ctx, addr interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LockedCoins", reflect.TypeOf((*MockBankKeeper)(nil).LockedCoins), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LockedCoins", reflect.TypeOf((*MockBankKeeper)(nil).LockedCoins), ctx, addr)
 }
 
-// SendCoinsFromAccountToModule mocks base method
-func (m *MockBankKeeper) SendCoinsFromAccountToModule(arg0 types.Context, arg1 types.AccAddress, arg2 string, arg3 types.Coins) error {
+// SendCoinsFromAccountToModule mocks base method.
+func (m *MockBankKeeper) SendCoinsFromAccountToModule(ctx types.Context, senderAddr types.AccAddress, recipientModule string, amt types.Coins) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "SendCoinsFromAccountToModule", arg0, arg1, arg2, arg3)
+	ret := m.ctrl.Call(m, "SendCoinsFromAccountToModule", ctx, senderAddr, recipientModule, amt)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
-// SendCoinsFromAccountToModule indicates an expected call of SendCoinsFromAccountToModule
-func (mr *MockBankKeeperMockRecorder) SendCoinsFromAccountToModule(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
+// SendCoinsFromAccountToModule indicates an expected call of SendCoinsFromAccountToModule.
+func (mr *MockBankKeeperMockRecorder) SendCoinsFromAccountToModule(ctx, senderAddr, recipientModule, amt interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromAccountToModule", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromAccountToModule), arg0, arg1, arg2, arg3)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromAccountToModule", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromAccountToModule), ctx, senderAddr, recipientModule, amt)
 }
 
-// SendCoinsFromModuleToAccount mocks base method
-func (m *MockBankKeeper) SendCoinsFromModuleToAccount(arg0 types.Context, arg1 string, arg2 types.AccAddress, arg3 types.Coins) error {
+// SendCoinsFromModuleToAccount mocks base method.
+func (m *MockBankKeeper) SendCoinsFromModuleToAccount(ctx types.Context, senderModule string, recipientAddr types.AccAddress, amt types.Coins) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "SendCoinsFromModuleToAccount", arg0, arg1, arg2, arg3)
+	ret := m.ctrl.Call(m, "SendCoinsFromModuleToAccount", ctx, senderModule, recipientAddr, amt)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
-// SendCoinsFromModuleToAccount indicates an expected call of SendCoinsFromModuleToAccount
-func (mr *MockBankKeeperMockRecorder) SendCoinsFromModuleToAccount(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
+// SendCoinsFromModuleToAccount indicates an expected call of SendCoinsFromModuleToAccount.
+func (mr *MockBankKeeperMockRecorder) SendCoinsFromModuleToAccount(ctx, senderModule, recipientAddr, amt interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromModuleToAccount", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromModuleToAccount), arg0, arg1, arg2, arg3)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromModuleToAccount", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromModuleToAccount), ctx, senderModule, recipientAddr, amt)
 }
 
-// SpendableCoins mocks base method
-func (m *MockBankKeeper) SpendableCoins(arg0 types.Context, arg1 types.AccAddress) types.Coins {
+// SpendableCoins mocks base method.
+func (m *MockBankKeeper) SpendableCoins(ctx types.Context, addr types.AccAddress) types.Coins {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "SpendableCoins", arg0, arg1)
+	ret := m.ctrl.Call(m, "SpendableCoins", ctx, addr)
 	ret0, _ := ret[0].(types.Coins)
 	return ret0
 }
 
-// SpendableCoins indicates an expected call of SpendableCoins
-func (mr *MockBankKeeperMockRecorder) SpendableCoins(arg0, arg1 interface{}) *gomock.Call {
+// SpendableCoins indicates an expected call of SpendableCoins.
+func (mr *MockBankKeeperMockRecorder) SpendableCoins(ctx, addr interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpendableCoins", reflect.TypeOf((*MockBankKeeper)(nil).SpendableCoins), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpendableCoins", reflect.TypeOf((*MockBankKeeper)(nil).SpendableCoins), ctx, addr)
 }
 
-// MockAuthzKeeper is a mock of AuthzKeeper interface
+// MockAuthzKeeper is a mock of AuthzKeeper interface.
 type MockAuthzKeeper struct {
 	ctrl     *gomock.Controller
 	recorder *MockAuthzKeeperMockRecorder
 }
 
-// MockAuthzKeeperMockRecorder is the mock recorder for MockAuthzKeeper
+// MockAuthzKeeperMockRecorder is the mock recorder for MockAuthzKeeper.
 type MockAuthzKeeperMockRecorder struct {
 	mock *MockAuthzKeeper
 }
 
-// NewMockAuthzKeeper creates a new mock instance
+// NewMockAuthzKeeper creates a new mock instance.
 func NewMockAuthzKeeper(ctrl *gomock.Controller) *MockAuthzKeeper {
 	mock := &MockAuthzKeeper{ctrl: ctrl}
 	mock.recorder = &MockAuthzKeeperMockRecorder{mock}
 	return mock
 }
 
-// EXPECT returns an object that allows the caller to indicate expected use
+// EXPECT returns an object that allows the caller to indicate expected use.
 func (m *MockAuthzKeeper) EXPECT() *MockAuthzKeeperMockRecorder {
 	return m.recorder
 }
 
-// DeleteGrant mocks base method
-func (m *MockAuthzKeeper) DeleteGrant(arg0 types.Context, arg1, arg2 types.AccAddress, arg3 string) error {
+// DeleteGrant mocks base method.
+func (m *MockAuthzKeeper) DeleteGrant(ctx types.Context, grantee, granter types.AccAddress, msgType string) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "DeleteGrant", arg0, arg1, arg2, arg3)
+	ret := m.ctrl.Call(m, "DeleteGrant", ctx, grantee, granter, msgType)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
-// DeleteGrant indicates an expected call of DeleteGrant
-func (mr *MockAuthzKeeperMockRecorder) DeleteGrant(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
+// DeleteGrant indicates an expected call of DeleteGrant.
+func (mr *MockAuthzKeeperMockRecorder) DeleteGrant(ctx, grantee, granter, msgType interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteGrant", reflect.TypeOf((*MockAuthzKeeper)(nil).DeleteGrant), arg0, arg1, arg2, arg3)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteGrant", reflect.TypeOf((*MockAuthzKeeper)(nil).DeleteGrant), ctx, grantee, granter, msgType)
 }
 
-// GetGrant mocks base method
-func (m *MockAuthzKeeper) GetGrant(arg0 types.Context, arg1, arg2 types.AccAddress, arg3 string) (authz.Grant, bool) {
+// GetGrant mocks base method.
+func (m *MockAuthzKeeper) GetGrant(ctx types.Context, grantee, granter types.AccAddress, msgType string) (authz.Grant, bool) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetGrant", arg0, arg1, arg2, arg3)
+	ret := m.ctrl.Call(m, "GetGrant", ctx, grantee, granter, msgType)
 	ret0, _ := ret[0].(authz.Grant)
 	ret1, _ := ret[1].(bool)
 	return ret0, ret1
 }
 
-// GetGrant indicates an expected call of GetGrant
-func (mr *MockAuthzKeeperMockRecorder) GetGrant(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
+// GetGrant indicates an expected call of GetGrant.
+func (mr *MockAuthzKeeperMockRecorder) GetGrant(ctx, grantee, granter, msgType interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGrant", reflect.TypeOf((*MockAuthzKeeper)(nil).GetGrant), arg0, arg1, arg2, arg3)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGrant", reflect.TypeOf((*MockAuthzKeeper)(nil).GetGrant), ctx, grantee, granter, msgType)
 }
 
-// Update mocks base method
-func (m *MockAuthzKeeper) Update(arg0 types.Context, arg1, arg2 types.AccAddress, arg3 authz.Authorization) error {
+// Update mocks base method.
+func (m *MockAuthzKeeper) Update(ctx types.Context, grantee, granter types.AccAddress, updated authz.Authorization) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "Update", arg0, arg1, arg2, arg3)
+	ret := m.ctrl.Call(m, "Update", ctx, grantee, granter, updated)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
-// Update indicates an expected call of Update
-func (mr *MockAuthzKeeperMockRecorder) Update(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
+// Update indicates an expected call of Update.
+func (mr *MockAuthzKeeperMockRecorder) Update(ctx, grantee, granter, updated interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockAuthzKeeper)(nil).Update), arg0, arg1, arg2, arg3)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockAuthzKeeper)(nil).Update), ctx, grantee, granter, updated)
 }
diff --git a/x/sp/types/keys.go b/x/sp/types/keys.go
index 49c3b61c5..858f09052 100644
--- a/x/sp/types/keys.go
+++ b/x/sp/types/keys.go
@@ -25,18 +25,28 @@ var (
 	ParamsKey = []byte{0x01}
 
 	StorageProviderKey               = []byte{0x21} // prefix for each key to a storage provider
-	StorageProviderByFundingAddrKey  = []byte{0x22} // prefix for each key to a storage provider index, by funding address
-	StorageProviderBySealAddrKey     = []byte{0x23} // prefix for each key to a storage provider index, by seal address
-	StorageProviderByApprovalAddrKey = []byte{0x24} // prefix for each key to a storage provider index, by approval address
-	StorageProviderByGcAddrKey       = []byte{0x25} // prefix for each key to a storage provider index, by gc address
-	SpStoragePriceKeyPrefix          = []byte{0x26}
-	SecondarySpStorePriceKeyPrefix   = []byte{0x27}
+	StorageProviderByOperatorAddrKey = []byte{0x23} // prefix for each key to a storage provider index, by operator address
+	StorageProviderByFundingAddrKey  = []byte{0x24} // prefix for each key to a storage provider index, by funding address
+	StorageProviderBySealAddrKey     = []byte{0x25} // prefix for each key to a storage provider index, by seal address
+	StorageProviderByApprovalAddrKey = []byte{0x26} // prefix for each key to a storage provider index, by approval address
+	StorageProviderByGcAddrKey       = []byte{0x27} // prefix for each key to a storage provider index, by gc address
+	SpStoragePriceKeyPrefix          = []byte{0x28}
+	SecondarySpStorePriceKeyPrefix   = []byte{0x29}
+	StorageProviderByBlsPubKeyKey    = []byte{0x30} // prefix for each key to a storage provider index, by bls pub key
+
+	StorageProviderSequenceKey = []byte{0x31}
 )
 
 // GetStorageProviderKey creates the key for the provider with address
 // VALUE: staking/Validator
-func GetStorageProviderKey(spAddr sdk.AccAddress) []byte {
-	return append(StorageProviderKey, spAddr.Bytes()...)
+func GetStorageProviderKey(id []byte) []byte {
+	return append(StorageProviderKey, id...)
+
+}
+
+func GetStorageProviderByOperatorAddrKey(operatorAddr sdk.AccAddress) []byte {
+	return append(StorageProviderByOperatorAddrKey, operatorAddr.Bytes()...)
+
 }
 
 // GetStorageProviderByFundingAddrKey creates the key for the storage provider with funding address
@@ -57,12 +67,17 @@ func GetStorageProviderByApprovalAddrKey(spAddr sdk.AccAddress) []byte {
 	return append(StorageProviderByApprovalAddrKey, spAddr.Bytes()...)
 }
 
-// GetStorageProviderByApprovalAddrKey creates the key for the storage provider with approval address
+// GetStorageProviderByGcAddrKey creates the key for the storage provider with approval address
 // VALUE: storage provider operator address ([]byte)
 func GetStorageProviderByGcAddrKey(spAddr sdk.AccAddress) []byte {
 	return append(StorageProviderByGcAddrKey, spAddr.Bytes()...)
 }
 
+// GetStorageProviderByBlsKeyKey creates the key for the storage provider with bls pub key
+func GetStorageProviderByBlsKeyKey(blsPk []byte) []byte {
+	return append(StorageProviderByBlsPubKeyKey, blsPk...)
+}
+
 func UnmarshalStorageProvider(cdc codec.BinaryCodec, value []byte) (sp *StorageProvider, err error) {
 	sp = &StorageProvider{}
 	err = cdc.Unmarshal(value, sp)
@@ -83,35 +98,37 @@ func MustMarshalStorageProvider(cdc codec.BinaryCodec, sp *StorageProvider) []by
 }
 
 func SpStoragePriceKey(
-	sp sdk.AccAddress,
-	UpdateTimeSec int64,
+	spId uint32,
+	timestamp int64,
 ) []byte {
+	idBytes := make([]byte, 4)
+	binary.BigEndian.PutUint32(idBytes, spId)
+
 	timeBytes := make([]byte, 8)
-	binary.BigEndian.PutUint64(timeBytes, uint64(UpdateTimeSec))
+	binary.BigEndian.PutUint64(timeBytes, uint64(timestamp))
 
 	var key []byte
-	key = append(key, sp...)
+	key = append(key, idBytes...)
 	key = append(key, timeBytes...)
 
 	return key
 }
 
-func ParseSpStoragePriceKey(key []byte) (spAddr sdk.AccAddress, UpdateTimeSec int64) {
-	length := len(key)
-	spAddr = key[:length-8]
-	UpdateTimeSec = int64(binary.BigEndian.Uint64(key[length-8 : length]))
+func ParseSpStoragePriceKey(key []byte) (spId uint32, timestamp int64) {
+	spId = binary.BigEndian.Uint32(key[0:4])
+	timestamp = int64(binary.BigEndian.Uint64(key[4:]))
 	return
 }
 
 func SecondarySpStorePriceKey(
-	UpdateTimeSec int64,
+	timestamp int64,
 ) []byte {
 	timeBytes := make([]byte, 8)
-	binary.BigEndian.PutUint64(timeBytes, uint64(UpdateTimeSec))
+	binary.BigEndian.PutUint64(timeBytes, uint64(timestamp))
 	return timeBytes
 }
 
-func ParseSecondarySpStorePriceKey(key []byte) (UpdateTimeSec int64) {
-	UpdateTimeSec = int64(binary.BigEndian.Uint64(key))
+func ParseSecondarySpStorePriceKey(key []byte) (timestamp int64) {
+	timestamp = int64(binary.BigEndian.Uint64(key))
 	return
 }
diff --git a/x/sp/types/message.go b/x/sp/types/message.go
index 63a3c71cb..c50881a27 100644
--- a/x/sp/types/message.go
+++ b/x/sp/types/message.go
@@ -1,9 +1,14 @@
 package types
 
 import (
+	"encoding/hex"
+
 	"cosmossdk.io/errors"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	"github.com/prysmaticlabs/prysm/crypto/bls"
+
+	gnfderrors "github.com/bnb-chain/greenfield/types/errors"
 )
 
 const (
@@ -11,6 +16,7 @@ const (
 	TypeMsgEditStorageProvider   = "edit_storage_provider"
 	TypeMsgDeposit               = "deposit"
 	TypeMsgUpdateSpStoragePrice  = "update_sp_storage_price"
+	TypeMsgUpdateParams          = "update_params"
 )
 
 var (
@@ -26,13 +32,15 @@ var (
 // creator is the module account of gov module
 // SpAddress is the account address of storage provider
 // fundAddress is another accoutn address of storage provider which used to deposit or rewarding
+// blsKey is the public key of bls private key, which is used for sealing object and completing migration signature.
+// blsProof is the signature signed via bls private key on bls public key bytes
 func NewMsgCreateStorageProvider(
-	creator sdk.AccAddress, SpAddress sdk.AccAddress, fundingAddress sdk.AccAddress,
+	creator sdk.AccAddress, spAddress sdk.AccAddress, fundingAddress sdk.AccAddress,
 	sealAddress sdk.AccAddress, approvalAddress sdk.AccAddress, gcAddress sdk.AccAddress,
-	description Description, endpoint string, deposit sdk.Coin, readPrice sdk.Dec, freeReadQuota uint64, storePrice sdk.Dec) (*MsgCreateStorageProvider, error) {
+	description Description, endpoint string, deposit sdk.Coin, readPrice sdk.Dec, freeReadQuota uint64, storePrice sdk.Dec, blsKey, blsProof string) (*MsgCreateStorageProvider, error) {
 	return &MsgCreateStorageProvider{
 		Creator:         creator.String(),
-		SpAddress:       SpAddress.String(),
+		SpAddress:       spAddress.String(),
 		FundingAddress:  fundingAddress.String(),
 		SealAddress:     sealAddress.String(),
 		ApprovalAddress: approvalAddress.String(),
@@ -43,6 +51,8 @@ func NewMsgCreateStorageProvider(
 		ReadPrice:       readPrice,
 		FreeReadQuota:   freeReadQuota,
 		StorePrice:      storePrice,
+		BlsKey:          blsKey,
+		BlsProof:        blsProof,
 	}, nil
 }
 
@@ -91,6 +101,22 @@ func (msg *MsgCreateStorageProvider) ValidateBasic() error {
 	if _, err := sdk.AccAddressFromHexUnsafe(msg.GcAddress); err != nil {
 		return errors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid gc address (%s)", err)
 	}
+
+	blsPk, err := hex.DecodeString(msg.BlsKey)
+	if err != nil || len(blsPk) != sdk.BLSPubKeyLength {
+		return errors.Wrapf(sdkerrors.ErrInvalidPubKey, "invalid bls pub key")
+	}
+
+	blsProof, err := hex.DecodeString(msg.BlsProof)
+	if err != nil || len(blsProof) != sdk.BLSSignatureLength {
+		return errors.Wrapf(gnfderrors.ErrInvalidBlsSignature, "invalid bls sig")
+	}
+
+	_, err = bls.SignatureFromBytes(blsProof)
+	if err != nil {
+		return errors.Wrapf(sdkerrors.ErrorInvalidSigner, "invalid bls signature")
+	}
+
 	if !msg.Deposit.IsValid() || !msg.Deposit.Amount.IsPositive() {
 		return errors.Wrap(sdkerrors.ErrInvalidRequest, "invalid deposit amount")
 	}
@@ -99,7 +125,7 @@ func (msg *MsgCreateStorageProvider) ValidateBasic() error {
 		return errors.Wrap(sdkerrors.ErrInvalidRequest, "empty description")
 	}
 
-	err := IsValidEndpointURL(msg.Endpoint)
+	err = IsValidEndpointURL(msg.Endpoint)
 	if err != nil {
 		return errors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid endpoint (%s)", err)
 	}
@@ -111,7 +137,7 @@ func (msg *MsgCreateStorageProvider) ValidateBasic() error {
 
 // NewMsgEditStorageProvider creates a new MsgEditStorageProvider instance
 func NewMsgEditStorageProvider(spAddress sdk.AccAddress, endpoint string, description *Description,
-	sealAddress sdk.AccAddress, approvalAddress sdk.AccAddress, gcAddress sdk.AccAddress) *MsgEditStorageProvider {
+	sealAddress sdk.AccAddress, approvalAddress sdk.AccAddress, gcAddress sdk.AccAddress, blsKey, blsProof string) *MsgEditStorageProvider {
 	return &MsgEditStorageProvider{
 		SpAddress:       spAddress.String(),
 		Endpoint:        endpoint,
@@ -119,6 +145,8 @@ func NewMsgEditStorageProvider(spAddress sdk.AccAddress, endpoint string, descri
 		SealAddress:     sealAddress.String(),
 		ApprovalAddress: approvalAddress.String(),
 		GcAddress:       gcAddress.String(),
+		BlsKey:          blsKey,
+		BlsProof:        blsProof,
 	}
 }
 
@@ -182,6 +210,23 @@ func (msg *MsgEditStorageProvider) ValidateBasic() error {
 			return errors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid gc address (%s)", err)
 		}
 	}
+	if msg.BlsKey != "" {
+		if msg.BlsProof == "" {
+			return errors.Wrapf(gnfderrors.ErrInvalidBlsSignature, "bls proof is not provided")
+		}
+		blsPk, err := hex.DecodeString(msg.BlsKey)
+		if err != nil || len(blsPk) != sdk.BLSPubKeyLength {
+			return errors.Wrapf(sdkerrors.ErrInvalidPubKey, "invalid bls pub key")
+		}
+		blsProof, err := hex.DecodeString(msg.BlsProof)
+		if err != nil || len(blsProof) != sdk.BLSSignatureLength {
+			return errors.Wrapf(sdkerrors.ErrorInvalidSigner, "invalid bls signature")
+		}
+		_, err = bls.SignatureFromBytes(blsProof)
+		if err != nil {
+			return errors.Wrapf(sdkerrors.ErrorInvalidSigner, "invalid bls signature")
+		}
+	}
 	return nil
 }
 
@@ -271,8 +316,6 @@ func (msg *MsgUpdateSpStoragePrice) ValidateBasic() error {
 	return nil
 }
 
-var _ sdk.Msg = &MsgUpdateParams{}
-
 // GetSignBytes implements the LegacyMsg interface.
 func (m MsgUpdateParams) GetSignBytes() []byte {
 	return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&m))
diff --git a/x/sp/types/message_test.go b/x/sp/types/message_test.go
index a38d618f0..d2b4e6507 100644
--- a/x/sp/types/message_test.go
+++ b/x/sp/types/message_test.go
@@ -7,6 +7,8 @@ import (
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 	"github.com/stretchr/testify/require"
+
+	"github.com/bnb-chain/greenfield/testutil/sample"
 )
 
 var (
@@ -17,15 +19,18 @@ var (
 func TestMsgCreateStorageProvider_ValidateBasic(t *testing.T) {
 	pk1 := ed25519.GenPrivKey().PubKey()
 	spAddr := sdk.AccAddress(pk1.Address())
+	blsPubKey, blsProof := sample.RandBlsPubKeyAndBlsProof()
+
 	tests := []struct {
 		name, moniker, identity, website, details                                   string
 		creator, spAddress, fundingAddress, sealAddress, approvalAddress, gcAddress sdk.AccAddress
+		blsKey, blsProof                                                            string
 		deposit                                                                     sdk.Coin
 		err                                                                         error
 	}{
-		{"basic", "a", "b", "c", "d", spAddr, spAddr, spAddr, spAddr, spAddr, spAddr, coinPos, nil},
-		{"basic_empty", "a", "b", "c", "d", sdk.AccAddress{}, spAddr, spAddr, spAddr, spAddr, spAddr, coinPos, sdkerrors.ErrInvalidAddress},
-		{"zero deposit", "a", "b", "c", "d", spAddr, spAddr, spAddr, spAddr, spAddr, spAddr, coinZero, nil},
+		{"basic", "a", "b", "c", "d", spAddr, spAddr, spAddr, spAddr, spAddr, spAddr, blsPubKey, blsProof, coinPos, nil},
+		{"basic_empty", "a", "b", "c", "d", sdk.AccAddress{}, spAddr, spAddr, spAddr, spAddr, spAddr, blsPubKey, blsProof, coinPos, sdkerrors.ErrInvalidAddress},
+		{"zero deposit", "a", "b", "c", "d", spAddr, spAddr, spAddr, spAddr, spAddr, spAddr, blsPubKey, blsProof, coinZero, nil},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -37,6 +42,8 @@ func TestMsgCreateStorageProvider_ValidateBasic(t *testing.T) {
 				SealAddress:     tt.sealAddress.String(),
 				ApprovalAddress: tt.approvalAddress.String(),
 				GcAddress:       tt.gcAddress.String(),
+				BlsKey:          tt.blsKey,
+				BlsProof:        tt.blsProof,
 				Endpoint:        "http://127.0.0.1:9033",
 				StorePrice:      sdk.ZeroDec(),
 				ReadPrice:       sdk.ZeroDec(),
@@ -55,13 +62,16 @@ func TestMsgCreateStorageProvider_ValidateBasic(t *testing.T) {
 func TestMsgEditStorageProvider_ValidateBasic(t *testing.T) {
 	pk1 := ed25519.GenPrivKey().PubKey()
 	spAddr := sdk.AccAddress(pk1.Address())
+	blsPubKey, blsProof := sample.RandBlsPubKeyAndBlsProof()
+
 	tests := []struct {
 		name, moniker, identity, website, details string
 		spAddress                                 sdk.AccAddress
+		blsKey, blsProof                          string
 		err                                       error
 	}{
-		{"basic", "a1", "b1", "c1", "d1", spAddr, nil},
-		{"empty", "", "", "", "", spAddr, sdkerrors.ErrInvalidRequest},
+		{"basic", "a1", "b1", "c1", "d1", spAddr, blsPubKey, blsProof, nil},
+		{"empty", "", "", "", "", spAddr, blsPubKey, blsProof, sdkerrors.ErrInvalidRequest},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
diff --git a/x/sp/types/params.go b/x/sp/types/params.go
index 16e53d4b6..fcefd6e85 100644
--- a/x/sp/types/params.go
+++ b/x/sp/types/params.go
@@ -26,7 +26,7 @@ var (
 )
 
 var (
-	KeyDepostDenom                = []byte("DepositDenom")
+	KeyDepositDenom               = []byte("DepositDenom")
 	KeyMinDeposit                 = []byte("MinDeposit")
 	KeySecondarySpStorePriceRatio = []byte("SecondarySpStorePriceRatio")
 )
@@ -55,7 +55,7 @@ func DefaultParams() Params {
 // ParamSetPairs get the params.ParamSet
 func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs {
 	return paramtypes.ParamSetPairs{
-		paramtypes.NewParamSetPair(KeyDepostDenom, &p.DepositDenom, validateDepositDenom),
+		paramtypes.NewParamSetPair(KeyDepositDenom, &p.DepositDenom, validateDepositDenom),
 		paramtypes.NewParamSetPair(KeyMinDeposit, &p.MinDeposit, validateMinDeposit),
 		paramtypes.NewParamSetPair(KeySecondarySpStorePriceRatio, &p.SecondarySpStorePriceRatio, validateSecondarySpStorePriceRatio),
 	}
diff --git a/x/sp/types/query.pb.go b/x/sp/types/query.pb.go
index a414b449d..63cfe4f2f 100644
--- a/x/sp/types/query.pb.go
+++ b/x/sp/types/query.pb.go
@@ -409,7 +409,7 @@ func (m *QueryGetSecondarySpStorePriceByTimeResponse) GetSecondarySpStorePrice()
 }
 
 type QueryStorageProviderRequest struct {
-	SpAddress string `protobuf:"bytes,1,opt,name=spAddress,proto3" json:"spAddress,omitempty"`
+	Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
 }
 
 func (m *QueryStorageProviderRequest) Reset()         { *m = QueryStorageProviderRequest{} }
@@ -445,11 +445,11 @@ func (m *QueryStorageProviderRequest) XXX_DiscardUnknown() {
 
 var xxx_messageInfo_QueryStorageProviderRequest proto.InternalMessageInfo
 
-func (m *QueryStorageProviderRequest) GetSpAddress() string {
+func (m *QueryStorageProviderRequest) GetId() uint32 {
 	if m != nil {
-		return m.SpAddress
+		return m.Id
 	}
-	return ""
+	return 0
 }
 
 type QueryStorageProviderResponse struct {
@@ -496,6 +496,102 @@ func (m *QueryStorageProviderResponse) GetStorageProvider() *StorageProvider {
 	return nil
 }
 
+type QueryStorageProviderByOperatorAddressRequest struct {
+	OperatorAddress string `protobuf:"bytes,1,opt,name=operator_address,json=operatorAddress,proto3" json:"operator_address,omitempty"`
+}
+
+func (m *QueryStorageProviderByOperatorAddressRequest) Reset() {
+	*m = QueryStorageProviderByOperatorAddressRequest{}
+}
+func (m *QueryStorageProviderByOperatorAddressRequest) String() string {
+	return proto.CompactTextString(m)
+}
+func (*QueryStorageProviderByOperatorAddressRequest) ProtoMessage() {}
+func (*QueryStorageProviderByOperatorAddressRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_48dd9c8aad3b7a6d, []int{10}
+}
+func (m *QueryStorageProviderByOperatorAddressRequest) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *QueryStorageProviderByOperatorAddressRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_QueryStorageProviderByOperatorAddressRequest.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *QueryStorageProviderByOperatorAddressRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_QueryStorageProviderByOperatorAddressRequest.Merge(m, src)
+}
+func (m *QueryStorageProviderByOperatorAddressRequest) XXX_Size() int {
+	return m.Size()
+}
+func (m *QueryStorageProviderByOperatorAddressRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_QueryStorageProviderByOperatorAddressRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryStorageProviderByOperatorAddressRequest proto.InternalMessageInfo
+
+func (m *QueryStorageProviderByOperatorAddressRequest) GetOperatorAddress() string {
+	if m != nil {
+		return m.OperatorAddress
+	}
+	return ""
+}
+
+type QueryStorageProviderByOperatorAddressResponse struct {
+	StorageProvider *StorageProvider `protobuf:"bytes,1,opt,name=storageProvider,proto3" json:"storageProvider,omitempty"`
+}
+
+func (m *QueryStorageProviderByOperatorAddressResponse) Reset() {
+	*m = QueryStorageProviderByOperatorAddressResponse{}
+}
+func (m *QueryStorageProviderByOperatorAddressResponse) String() string {
+	return proto.CompactTextString(m)
+}
+func (*QueryStorageProviderByOperatorAddressResponse) ProtoMessage() {}
+func (*QueryStorageProviderByOperatorAddressResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_48dd9c8aad3b7a6d, []int{11}
+}
+func (m *QueryStorageProviderByOperatorAddressResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *QueryStorageProviderByOperatorAddressResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_QueryStorageProviderByOperatorAddressResponse.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *QueryStorageProviderByOperatorAddressResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_QueryStorageProviderByOperatorAddressResponse.Merge(m, src)
+}
+func (m *QueryStorageProviderByOperatorAddressResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *QueryStorageProviderByOperatorAddressResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_QueryStorageProviderByOperatorAddressResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryStorageProviderByOperatorAddressResponse proto.InternalMessageInfo
+
+func (m *QueryStorageProviderByOperatorAddressResponse) GetStorageProvider() *StorageProvider {
+	if m != nil {
+		return m.StorageProvider
+	}
+	return nil
+}
+
 func init() {
 	proto.RegisterType((*QueryParamsRequest)(nil), "greenfield.sp.QueryParamsRequest")
 	proto.RegisterType((*QueryParamsResponse)(nil), "greenfield.sp.QueryParamsResponse")
@@ -507,58 +603,66 @@ func init() {
 	proto.RegisterType((*QueryGetSecondarySpStorePriceByTimeResponse)(nil), "greenfield.sp.QueryGetSecondarySpStorePriceByTimeResponse")
 	proto.RegisterType((*QueryStorageProviderRequest)(nil), "greenfield.sp.QueryStorageProviderRequest")
 	proto.RegisterType((*QueryStorageProviderResponse)(nil), "greenfield.sp.QueryStorageProviderResponse")
+	proto.RegisterType((*QueryStorageProviderByOperatorAddressRequest)(nil), "greenfield.sp.QueryStorageProviderByOperatorAddressRequest")
+	proto.RegisterType((*QueryStorageProviderByOperatorAddressResponse)(nil), "greenfield.sp.QueryStorageProviderByOperatorAddressResponse")
 }
 
 func init() { proto.RegisterFile("greenfield/sp/query.proto", fileDescriptor_48dd9c8aad3b7a6d) }
 
 var fileDescriptor_48dd9c8aad3b7a6d = []byte{
-	// 733 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x55, 0xc1, 0x4f, 0xd4, 0x4e,
-	0x14, 0xde, 0xc2, 0xef, 0xb7, 0x86, 0x21, 0x0a, 0x19, 0x21, 0x40, 0x65, 0x8b, 0x56, 0x54, 0xb2,
-	0x68, 0x0b, 0xcb, 0x49, 0x3d, 0x18, 0xd0, 0x88, 0x92, 0x98, 0xe0, 0xe2, 0xc9, 0x98, 0x34, 0xd3,
-	0xed, 0x50, 0x9a, 0xb0, 0x9d, 0xa1, 0xd3, 0x25, 0x6e, 0x08, 0x17, 0xff, 0x02, 0xa3, 0x9e, 0x8c,
-	0x7f, 0x10, 0x47, 0xa2, 0x17, 0x4f, 0xc6, 0x80, 0x5e, 0xfc, 0x2b, 0x4c, 0x67, 0xa6, 0xbb, 0xdb,
-	0xb1, 0xdd, 0x5d, 0xbd, 0xed, 0xce, 0xfb, 0xde, 0x7b, 0xdf, 0xf7, 0xe6, 0x7d, 0x53, 0x30, 0xe7,
-	0x47, 0x18, 0x87, 0xbb, 0x01, 0xde, 0xf7, 0x6c, 0x46, 0xed, 0x83, 0x16, 0x8e, 0xda, 0x16, 0x8d,
-	0x48, 0x4c, 0xe0, 0xc5, 0x6e, 0xc8, 0x62, 0x54, 0xaf, 0x36, 0x08, 0x6b, 0x12, 0x66, 0xbb, 0x88,
-	0x61, 0x81, 0xb3, 0x0f, 0x57, 0x5d, 0x1c, 0xa3, 0x55, 0x9b, 0x22, 0x3f, 0x08, 0x51, 0x1c, 0x90,
-	0x50, 0xa4, 0xea, 0x73, 0x02, 0xeb, 0xf0, 0x7f, 0xb6, 0xf8, 0x23, 0x43, 0x53, 0x3e, 0xf1, 0x89,
-	0x38, 0x4f, 0x7e, 0xc9, 0xd3, 0x79, 0x9f, 0x10, 0x7f, 0x1f, 0xdb, 0x88, 0x06, 0x36, 0x0a, 0x43,
-	0x12, 0xf3, 0x6a, 0x69, 0x8e, 0x9e, 0x25, 0x49, 0x51, 0x84, 0x9a, 0x69, 0x4c, 0x11, 0x10, 0xb7,
-	0x29, 0x96, 0x21, 0x73, 0x0a, 0xc0, 0xe7, 0x09, 0xcf, 0x6d, 0x8e, 0xaf, 0xe3, 0x83, 0x16, 0x66,
-	0xb1, 0xb9, 0x05, 0x2e, 0x67, 0x4e, 0x19, 0x25, 0x21, 0xc3, 0x70, 0x0d, 0x94, 0x45, 0xdd, 0x59,
-	0xed, 0xaa, 0xb6, 0x34, 0x5e, 0x9b, 0xb6, 0x32, 0xf2, 0x2d, 0x01, 0xdf, 0xf8, 0xef, 0xe4, 0xdb,
-	0x42, 0xa9, 0x2e, 0xa1, 0xe6, 0x2e, 0x98, 0xe7, 0xb5, 0x76, 0x62, 0x12, 0x21, 0x1f, 0x6f, 0x47,
-	0xe4, 0x30, 0xf0, 0x70, 0x94, 0xf6, 0x82, 0x8f, 0x01, 0xe8, 0xce, 0x46, 0x16, 0xbe, 0x69, 0xc9,
-	0x79, 0x24, 0x83, 0xb4, 0xc4, 0xc0, 0xe5, 0x20, 0xad, 0x6d, 0xe4, 0x63, 0x99, 0x5b, 0xef, 0xc9,
-	0x34, 0x3f, 0x6a, 0xa0, 0x52, 0xd0, 0x48, 0xd2, 0x5f, 0x01, 0xa3, 0x8c, 0x26, 0xdc, 0x47, 0x97,
-	0xc6, 0x6b, 0x86, 0xc2, 0x5d, 0xc9, 0xaa, 0x27, 0x50, 0xb8, 0x99, 0xe1, 0x36, 0xc2, 0xb9, 0xdd,
-	0x1a, 0xc8, 0x4d, 0xb4, 0xcb, 0x90, 0x7b, 0x05, 0xae, 0x73, 0x6e, 0x9b, 0x38, 0xde, 0xa1, 0x9d,
-	0x56, 0x41, 0x03, 0x6f, 0xb4, 0x5f, 0x04, 0xcd, 0x54, 0x0f, 0x9c, 0x01, 0x17, 0x18, 0x75, 0x90,
-	0xe7, 0x45, 0x7c, 0x10, 0x63, 0xf5, 0x32, 0xa3, 0xeb, 0x9e, 0x17, 0xc1, 0x79, 0x30, 0x16, 0x07,
-	0x4d, 0xcc, 0x62, 0xd4, 0xa4, 0x9c, 0xc7, 0x68, 0xbd, 0x7b, 0x60, 0xb6, 0xc0, 0x62, 0xff, 0xea,
-	0x72, 0x00, 0xcf, 0xc0, 0x24, 0xa3, 0x0e, 0x13, 0x00, 0x87, 0x26, 0x08, 0x39, 0xf0, 0x8a, 0x3a,
-	0x8d, 0x6c, 0x19, 0x71, 0xa3, 0x97, 0x58, 0xe6, 0xd4, 0xdc, 0x02, 0xd5, 0x4e, 0x5b, 0xdc, 0x20,
-	0xa1, 0x87, 0xa2, 0xb6, 0x48, 0xcc, 0xd3, 0x96, 0x91, 0xa0, 0xa9, 0x12, 0xde, 0x69, 0x60, 0x79,
-	0xa8, 0x62, 0x52, 0x4a, 0x03, 0xcc, 0xb2, 0x14, 0xe6, 0x48, 0x51, 0x59, 0x49, 0x8b, 0xaa, 0xa4,
-	0xdc, 0xaa, 0x42, 0xd9, 0x34, 0xcb, 0x0b, 0x9a, 0xf7, 0xc1, 0x95, 0xbc, 0x8d, 0xea, 0x51, 0x24,
-	0xae, 0x07, 0x33, 0x26, 0xef, 0xab, 0x7b, 0x60, 0xee, 0xe5, 0xef, 0x7d, 0x47, 0xc1, 0x13, 0x30,
-	0xc1, 0xb2, 0x21, 0x49, 0x7c, 0xd0, 0x66, 0xaa, 0x69, 0xb5, 0x5f, 0x65, 0xf0, 0x3f, 0x6f, 0x05,
-	0x43, 0x50, 0x16, 0x1e, 0x84, 0xd7, 0x94, 0x22, 0x7f, 0x9a, 0x5c, 0x37, 0xfb, 0x41, 0x04, 0x49,
-	0xb3, 0xf2, 0xe6, 0xcb, 0x8f, 0xf7, 0x23, 0x33, 0x70, 0xda, 0xce, 0x7b, 0x5e, 0xe0, 0x07, 0x0d,
-	0x4c, 0xaa, 0x76, 0x83, 0xcb, 0x79, 0x75, 0x0b, 0xdc, 0xaf, 0xdf, 0x1e, 0x0e, 0x2c, 0xe9, 0xdc,
-	0xe0, 0x74, 0x16, 0x60, 0x25, 0x43, 0xa7, 0xb3, 0xcf, 0x29, 0x83, 0xcf, 0x9a, 0x9c, 0x7d, 0x81,
-	0x21, 0x60, 0x2d, 0xaf, 0x6b, 0x7f, 0x6f, 0xea, 0x6b, 0x7f, 0x95, 0x23, 0x09, 0x3f, 0xe5, 0x84,
-	0x1f, 0xc2, 0x75, 0x65, 0x7e, 0x3e, 0x8e, 0x1d, 0xd5, 0x8a, 0x8e, 0xdb, 0x76, 0x12, 0x43, 0xd8,
-	0x47, 0xf2, 0x0d, 0x38, 0xb6, 0x8f, 0x3a, 0x06, 0x39, 0x86, 0x3f, 0xb5, 0x9e, 0x37, 0xa4, 0xd8,
-	0x21, 0xf0, 0x6e, 0x11, 0xcf, 0x81, 0x16, 0xd5, 0xef, 0xfd, 0x4b, 0xaa, 0x54, 0xba, 0xc9, 0x95,
-	0xae, 0xc3, 0x07, 0x79, 0x4a, 0x0b, 0x9c, 0xda, 0x55, 0xdc, 0xa3, 0xf3, 0x93, 0x06, 0x26, 0x94,
-	0x05, 0x80, 0xd5, 0x21, 0xb6, 0x24, 0x15, 0xb1, 0x3c, 0x14, 0x56, 0xb2, 0x5e, 0xe1, 0xac, 0xab,
-	0x70, 0xa9, 0xdf, 0x42, 0x25, 0x17, 0x22, 0x5d, 0x7d, 0xbc, 0xf1, 0xe8, 0xe4, 0xcc, 0xd0, 0x4e,
-	0xcf, 0x0c, 0xed, 0xfb, 0x99, 0xa1, 0xbd, 0x3d, 0x37, 0x4a, 0xa7, 0xe7, 0x46, 0xe9, 0xeb, 0xb9,
-	0x51, 0x7a, 0x59, 0xf5, 0x83, 0x78, 0xaf, 0xe5, 0x5a, 0x0d, 0xd2, 0xb4, 0xdd, 0xd0, 0xbd, 0xd3,
-	0xd8, 0x43, 0x41, 0xd8, 0x5b, 0xf7, 0x75, 0xe7, 0xe3, 0xeb, 0x96, 0xf9, 0xd7, 0x77, 0xed, 0x77,
-	0x00, 0x00, 0x00, 0xff, 0xff, 0xc1, 0xdd, 0x3b, 0x5a, 0x5b, 0x08, 0x00, 0x00,
+	// 819 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xdf, 0x4f, 0x13, 0x4b,
+	0x14, 0xee, 0x96, 0x7b, 0x7b, 0x2f, 0x43, 0xf8, 0x91, 0xb9, 0x10, 0x60, 0x2f, 0x2d, 0xb0, 0x70,
+	0xaf, 0x50, 0x60, 0x57, 0xda, 0xf8, 0xa0, 0x98, 0x18, 0xaa, 0x11, 0x25, 0x31, 0x62, 0xf1, 0x45,
+	0x63, 0xb2, 0xd9, 0x76, 0x87, 0x65, 0x13, 0xba, 0x33, 0xec, 0x6c, 0x89, 0x0d, 0xe1, 0xc5, 0xbf,
+	0xc0, 0xf8, 0xe3, 0xc5, 0xbf, 0xc6, 0x47, 0x1e, 0x89, 0xbe, 0x18, 0x1f, 0x8c, 0x01, 0xfd, 0x3f,
+	0xcc, 0xce, 0xcc, 0xb6, 0xdd, 0x71, 0xfb, 0x43, 0xe2, 0x5b, 0x3b, 0xe7, 0x3b, 0xe7, 0x7c, 0xdf,
+	0xe9, 0x39, 0x5f, 0x0a, 0xa6, 0x1d, 0x1f, 0x21, 0x6f, 0xcf, 0x45, 0x07, 0xb6, 0x41, 0x89, 0x71,
+	0x58, 0x47, 0x7e, 0x43, 0x27, 0x3e, 0x0e, 0x30, 0x1c, 0x6e, 0x85, 0x74, 0x4a, 0xd4, 0x7c, 0x15,
+	0xd3, 0x1a, 0xa6, 0x46, 0xc5, 0xa2, 0x88, 0xe3, 0x8c, 0xa3, 0xf5, 0x0a, 0x0a, 0xac, 0x75, 0x83,
+	0x58, 0x8e, 0xeb, 0x59, 0x81, 0x8b, 0x3d, 0x9e, 0xaa, 0x4e, 0x73, 0xac, 0xc9, 0xbe, 0x19, 0xfc,
+	0x8b, 0x08, 0x8d, 0x3b, 0xd8, 0xc1, 0xfc, 0x3d, 0xfc, 0x24, 0x5e, 0x67, 0x1c, 0x8c, 0x9d, 0x03,
+	0x64, 0x58, 0xc4, 0x35, 0x2c, 0xcf, 0xc3, 0x01, 0xab, 0x16, 0xe5, 0xa8, 0x71, 0x92, 0xc4, 0xf2,
+	0xad, 0x5a, 0x14, 0x93, 0x04, 0x04, 0x0d, 0x82, 0x44, 0x48, 0x1b, 0x07, 0xf0, 0x51, 0xc8, 0x73,
+	0x87, 0xe1, 0xcb, 0xe8, 0xb0, 0x8e, 0x68, 0xa0, 0x6d, 0x83, 0x7f, 0x62, 0xaf, 0x94, 0x60, 0x8f,
+	0x22, 0x58, 0x04, 0x19, 0x5e, 0x77, 0x4a, 0x99, 0x53, 0x96, 0x86, 0x0a, 0x13, 0x7a, 0x4c, 0xbe,
+	0xce, 0xe1, 0xa5, 0x3f, 0x4e, 0xbf, 0xcc, 0xa6, 0xca, 0x02, 0xaa, 0xed, 0x81, 0x19, 0x56, 0x6b,
+	0x37, 0xc0, 0xbe, 0xe5, 0xa0, 0x1d, 0x1f, 0x1f, 0xb9, 0x36, 0xf2, 0xa3, 0x5e, 0xf0, 0x2e, 0x00,
+	0xad, 0xd9, 0x88, 0xc2, 0xff, 0xeb, 0x62, 0x1e, 0xe1, 0x20, 0x75, 0x3e, 0x70, 0x31, 0x48, 0x7d,
+	0xc7, 0x72, 0x90, 0xc8, 0x2d, 0xb7, 0x65, 0x6a, 0xef, 0x14, 0x90, 0xed, 0xd0, 0x48, 0xd0, 0xbf,
+	0x0a, 0x06, 0x28, 0x09, 0xb9, 0x0f, 0x2c, 0x0d, 0x15, 0x72, 0x12, 0x77, 0x29, 0xab, 0x1c, 0x42,
+	0xe1, 0x56, 0x8c, 0x5b, 0x9a, 0x71, 0xbb, 0xd2, 0x93, 0x1b, 0x6f, 0x17, 0x23, 0xf7, 0x0c, 0x2c,
+	0x30, 0x6e, 0x5b, 0x28, 0xd8, 0x25, 0xcd, 0x56, 0x6e, 0x15, 0x95, 0x1a, 0x8f, 0xdd, 0x5a, 0xa4,
+	0x07, 0x4e, 0x82, 0xbf, 0x28, 0x31, 0x2d, 0xdb, 0xf6, 0xd9, 0x20, 0x06, 0xcb, 0x19, 0x4a, 0x36,
+	0x6d, 0xdb, 0x87, 0x33, 0x60, 0x30, 0x70, 0x6b, 0x88, 0x06, 0x56, 0x8d, 0x30, 0x1e, 0x03, 0xe5,
+	0xd6, 0x83, 0x56, 0x07, 0x8b, 0xdd, 0xab, 0x8b, 0x01, 0x3c, 0x00, 0x63, 0x94, 0x98, 0x94, 0x03,
+	0x4c, 0x12, 0x22, 0xc4, 0xc0, 0xb3, 0xf2, 0x34, 0xe2, 0x65, 0xf8, 0x2f, 0x3a, 0x42, 0x63, 0xaf,
+	0xda, 0x36, 0xc8, 0x37, 0xdb, 0xa2, 0x2a, 0xf6, 0x6c, 0xcb, 0x6f, 0xf0, 0xc4, 0x24, 0x6d, 0x31,
+	0x09, 0x8a, 0x2c, 0xe1, 0x95, 0x02, 0x56, 0xfa, 0x2a, 0x26, 0xa4, 0x54, 0xc1, 0x14, 0x8d, 0x60,
+	0xa6, 0x10, 0x15, 0x97, 0xb4, 0x28, 0x4b, 0x4a, 0xac, 0xca, 0x95, 0x4d, 0xd0, 0xa4, 0xa0, 0xb6,
+	0x06, 0xfe, 0x4d, 0xda, 0xa8, 0x48, 0xd1, 0x08, 0x48, 0xbb, 0x36, 0xeb, 0x36, 0x5c, 0x4e, 0xbb,
+	0xb6, 0xb6, 0x9f, 0xbc, 0xe9, 0x4d, 0xce, 0xf7, 0xc0, 0x28, 0x8d, 0x87, 0x04, 0xd5, 0x5e, 0xbb,
+	0x28, 0xa7, 0x69, 0x4f, 0xc0, 0x6a, 0x52, 0xa7, 0x52, 0xe3, 0x21, 0x41, 0xbe, 0x15, 0x60, 0x3f,
+	0x5c, 0x1a, 0x44, 0x9b, 0x37, 0xb6, 0x0c, 0xc6, 0xb0, 0x88, 0xb0, 0xed, 0x42, 0x94, 0x8a, 0x05,
+	0x1b, 0xc5, 0xf1, 0x0c, 0xad, 0x01, 0xd6, 0xfa, 0x2c, 0xfd, 0xbb, 0x55, 0x15, 0xde, 0xff, 0x0d,
+	0xfe, 0x64, 0xbd, 0xa1, 0x07, 0x32, 0xdc, 0x4b, 0xe0, 0xbc, 0x54, 0xe4, 0x67, 0xb3, 0x52, 0xb5,
+	0x6e, 0x10, 0x4e, 0x52, 0xcb, 0xbe, 0xf8, 0xf8, 0xed, 0x75, 0x7a, 0x12, 0x4e, 0x18, 0x49, 0x36,
+	0x09, 0xdf, 0x28, 0x60, 0x4c, 0xb6, 0x0d, 0xb8, 0x92, 0x54, 0xb7, 0x83, 0x8b, 0xa9, 0xab, 0xfd,
+	0x81, 0x05, 0x9d, 0xff, 0x18, 0x9d, 0x59, 0x98, 0x8d, 0xd1, 0x69, 0xde, 0x65, 0xc4, 0xe0, 0x83,
+	0x22, 0x36, 0xaa, 0xc3, 0x61, 0xc3, 0x42, 0x52, 0xd7, 0xee, 0x1e, 0xa3, 0x16, 0x7f, 0x29, 0x47,
+	0x10, 0xbe, 0xcf, 0x08, 0xdf, 0x86, 0x9b, 0xd2, 0xfc, 0x1c, 0x14, 0x98, 0xb2, 0xa5, 0x98, 0x95,
+	0x86, 0x19, 0x1e, 0xb6, 0x71, 0x2c, 0xbc, 0xec, 0xc4, 0x38, 0x6e, 0x1e, 0xfa, 0x09, 0xfc, 0xae,
+	0xb4, 0x79, 0x61, 0xe7, 0x4b, 0x87, 0xd7, 0x3b, 0xf1, 0xec, 0x69, 0x35, 0xea, 0x8d, 0xcb, 0xa4,
+	0x0a, 0xa5, 0x5b, 0x4c, 0xe9, 0x26, 0xbc, 0x95, 0xa4, 0xb4, 0x83, 0xe3, 0xb4, 0x14, 0xb7, 0xe9,
+	0x7c, 0xab, 0x80, 0x51, 0x69, 0x01, 0x60, 0xbe, 0x8f, 0x2d, 0x89, 0x44, 0xac, 0xf4, 0x85, 0x15,
+	0xac, 0x97, 0x19, 0xeb, 0x05, 0x38, 0xdf, 0x6d, 0xa1, 0x8c, 0x63, 0xd7, 0x3e, 0x81, 0x9f, 0x15,
+	0x30, 0xd7, 0xeb, 0xb8, 0xe1, 0x46, 0x1f, 0xcd, 0x3b, 0xb9, 0x8d, 0x7a, 0xf3, 0x72, 0xc9, 0x42,
+	0xca, 0x06, 0x93, 0x72, 0x0d, 0x16, 0xa5, 0x1f, 0x40, 0x56, 0x13, 0x0e, 0x5d, 0x36, 0xb5, 0xd2,
+	0x9d, 0xd3, 0xf3, 0x9c, 0x72, 0x76, 0x9e, 0x53, 0xbe, 0x9e, 0xe7, 0x94, 0x97, 0x17, 0xb9, 0xd4,
+	0xd9, 0x45, 0x2e, 0xf5, 0xe9, 0x22, 0x97, 0x7a, 0x9a, 0x77, 0xdc, 0x60, 0xbf, 0x5e, 0xd1, 0xab,
+	0xb8, 0x66, 0x54, 0xbc, 0xca, 0x5a, 0x75, 0xdf, 0x72, 0xbd, 0xf6, 0x16, 0xcf, 0x9b, 0x7f, 0x8d,
+	0x2a, 0x19, 0xf6, 0xdf, 0xa8, 0xf8, 0x23, 0x00, 0x00, 0xff, 0xff, 0x05, 0x6c, 0xf5, 0xa0, 0xf9,
+	0x09, 0x00, 0x00,
 }
 
 // Reference imports to suppress errors if they are not otherwise used.
@@ -581,8 +685,10 @@ type QueryClient interface {
 	QueryGetSpStoragePriceByTime(ctx context.Context, in *QueryGetSpStoragePriceByTimeRequest, opts ...grpc.CallOption) (*QueryGetSpStoragePriceByTimeResponse, error)
 	// get secondary store price by time
 	QueryGetSecondarySpStorePriceByTime(ctx context.Context, in *QueryGetSecondarySpStorePriceByTimeRequest, opts ...grpc.CallOption) (*QueryGetSecondarySpStorePriceByTimeResponse, error)
-	// Queries a storage provider with specify address
+	// Queries a storage provider with specify id
 	StorageProvider(ctx context.Context, in *QueryStorageProviderRequest, opts ...grpc.CallOption) (*QueryStorageProviderResponse, error)
+	// Queries a StorageProvider by specify operator address.
+	StorageProviderByOperatorAddress(ctx context.Context, in *QueryStorageProviderByOperatorAddressRequest, opts ...grpc.CallOption) (*QueryStorageProviderByOperatorAddressResponse, error)
 }
 
 type queryClient struct {
@@ -638,6 +744,15 @@ func (c *queryClient) StorageProvider(ctx context.Context, in *QueryStorageProvi
 	return out, nil
 }
 
+func (c *queryClient) StorageProviderByOperatorAddress(ctx context.Context, in *QueryStorageProviderByOperatorAddressRequest, opts ...grpc.CallOption) (*QueryStorageProviderByOperatorAddressResponse, error) {
+	out := new(QueryStorageProviderByOperatorAddressResponse)
+	err := c.cc.Invoke(ctx, "/greenfield.sp.Query/StorageProviderByOperatorAddress", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
 // QueryServer is the server API for Query service.
 type QueryServer interface {
 	// Parameters queries the parameters of the module.
@@ -648,8 +763,10 @@ type QueryServer interface {
 	QueryGetSpStoragePriceByTime(context.Context, *QueryGetSpStoragePriceByTimeRequest) (*QueryGetSpStoragePriceByTimeResponse, error)
 	// get secondary store price by time
 	QueryGetSecondarySpStorePriceByTime(context.Context, *QueryGetSecondarySpStorePriceByTimeRequest) (*QueryGetSecondarySpStorePriceByTimeResponse, error)
-	// Queries a storage provider with specify address
+	// Queries a storage provider with specify id
 	StorageProvider(context.Context, *QueryStorageProviderRequest) (*QueryStorageProviderResponse, error)
+	// Queries a StorageProvider by specify operator address.
+	StorageProviderByOperatorAddress(context.Context, *QueryStorageProviderByOperatorAddressRequest) (*QueryStorageProviderByOperatorAddressResponse, error)
 }
 
 // UnimplementedQueryServer can be embedded to have forward compatible implementations.
@@ -671,6 +788,9 @@ func (*UnimplementedQueryServer) QueryGetSecondarySpStorePriceByTime(ctx context
 func (*UnimplementedQueryServer) StorageProvider(ctx context.Context, req *QueryStorageProviderRequest) (*QueryStorageProviderResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method StorageProvider not implemented")
 }
+func (*UnimplementedQueryServer) StorageProviderByOperatorAddress(ctx context.Context, req *QueryStorageProviderByOperatorAddressRequest) (*QueryStorageProviderByOperatorAddressResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method StorageProviderByOperatorAddress not implemented")
+}
 
 func RegisterQueryServer(s grpc1.Server, srv QueryServer) {
 	s.RegisterService(&_Query_serviceDesc, srv)
@@ -766,6 +886,24 @@ func _Query_StorageProvider_Handler(srv interface{}, ctx context.Context, dec fu
 	return interceptor(ctx, in, info, handler)
 }
 
+func _Query_StorageProviderByOperatorAddress_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(QueryStorageProviderByOperatorAddressRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(QueryServer).StorageProviderByOperatorAddress(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/greenfield.sp.Query/StorageProviderByOperatorAddress",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(QueryServer).StorageProviderByOperatorAddress(ctx, req.(*QueryStorageProviderByOperatorAddressRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
 var _Query_serviceDesc = grpc.ServiceDesc{
 	ServiceName: "greenfield.sp.Query",
 	HandlerType: (*QueryServer)(nil),
@@ -790,6 +928,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{
 			MethodName: "StorageProvider",
 			Handler:    _Query_StorageProvider_Handler,
 		},
+		{
+			MethodName: "StorageProviderByOperatorAddress",
+			Handler:    _Query_StorageProviderByOperatorAddress_Handler,
+		},
 	},
 	Streams:  []grpc.StreamDesc{},
 	Metadata: "greenfield/sp/query.proto",
@@ -1084,12 +1226,10 @@ func (m *QueryStorageProviderRequest) MarshalToSizedBuffer(dAtA []byte) (int, er
 	_ = i
 	var l int
 	_ = l
-	if len(m.SpAddress) > 0 {
-		i -= len(m.SpAddress)
-		copy(dAtA[i:], m.SpAddress)
-		i = encodeVarintQuery(dAtA, i, uint64(len(m.SpAddress)))
+	if m.Id != 0 {
+		i = encodeVarintQuery(dAtA, i, uint64(m.Id))
 		i--
-		dAtA[i] = 0xa
+		dAtA[i] = 0x8
 	}
 	return len(dAtA) - i, nil
 }
@@ -1129,6 +1269,71 @@ func (m *QueryStorageProviderResponse) MarshalToSizedBuffer(dAtA []byte) (int, e
 	return len(dAtA) - i, nil
 }
 
+func (m *QueryStorageProviderByOperatorAddressRequest) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *QueryStorageProviderByOperatorAddressRequest) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryStorageProviderByOperatorAddressRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.OperatorAddress) > 0 {
+		i -= len(m.OperatorAddress)
+		copy(dAtA[i:], m.OperatorAddress)
+		i = encodeVarintQuery(dAtA, i, uint64(len(m.OperatorAddress)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *QueryStorageProviderByOperatorAddressResponse) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *QueryStorageProviderByOperatorAddressResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryStorageProviderByOperatorAddressResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.StorageProvider != nil {
+		{
+			size, err := m.StorageProvider.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintQuery(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
 func encodeVarintQuery(dAtA []byte, offset int, v uint64) int {
 	offset -= sovQuery(v)
 	base := offset
@@ -1248,14 +1453,39 @@ func (m *QueryStorageProviderRequest) Size() (n int) {
 	}
 	var l int
 	_ = l
-	l = len(m.SpAddress)
+	if m.Id != 0 {
+		n += 1 + sovQuery(uint64(m.Id))
+	}
+	return n
+}
+
+func (m *QueryStorageProviderResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.StorageProvider != nil {
+		l = m.StorageProvider.Size()
+		n += 1 + l + sovQuery(uint64(l))
+	}
+	return n
+}
+
+func (m *QueryStorageProviderByOperatorAddressRequest) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.OperatorAddress)
 	if l > 0 {
 		n += 1 + l + sovQuery(uint64(l))
 	}
 	return n
 }
 
-func (m *QueryStorageProviderResponse) Size() (n int) {
+func (m *QueryStorageProviderByOperatorAddressResponse) Size() (n int) {
 	if m == nil {
 		return 0
 	}
@@ -1978,9 +2208,164 @@ func (m *QueryStorageProviderRequest) Unmarshal(dAtA []byte) error {
 			return fmt.Errorf("proto: QueryStorageProviderRequest: illegal tag %d (wire type %d)", fieldNum, wire)
 		}
 		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType)
+			}
+			m.Id = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.Id |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipQuery(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *QueryStorageProviderResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowQuery
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: QueryStorageProviderResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: QueryStorageProviderResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field StorageProvider", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthQuery
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.StorageProvider == nil {
+				m.StorageProvider = &StorageProvider{}
+			}
+			if err := m.StorageProvider.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipQuery(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *QueryStorageProviderByOperatorAddressRequest) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowQuery
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: QueryStorageProviderByOperatorAddressRequest: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: QueryStorageProviderByOperatorAddressRequest: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
 		case 1:
 			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field SpAddress", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field OperatorAddress", wireType)
 			}
 			var stringLen uint64
 			for shift := uint(0); ; shift += 7 {
@@ -2008,7 +2393,7 @@ func (m *QueryStorageProviderRequest) Unmarshal(dAtA []byte) error {
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			m.SpAddress = string(dAtA[iNdEx:postIndex])
+			m.OperatorAddress = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
@@ -2031,7 +2416,7 @@ func (m *QueryStorageProviderRequest) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
-func (m *QueryStorageProviderResponse) Unmarshal(dAtA []byte) error {
+func (m *QueryStorageProviderByOperatorAddressResponse) Unmarshal(dAtA []byte) error {
 	l := len(dAtA)
 	iNdEx := 0
 	for iNdEx < l {
@@ -2054,10 +2439,10 @@ func (m *QueryStorageProviderResponse) Unmarshal(dAtA []byte) error {
 		fieldNum := int32(wire >> 3)
 		wireType := int(wire & 0x7)
 		if wireType == 4 {
-			return fmt.Errorf("proto: QueryStorageProviderResponse: wiretype end group for non-group")
+			return fmt.Errorf("proto: QueryStorageProviderByOperatorAddressResponse: wiretype end group for non-group")
 		}
 		if fieldNum <= 0 {
-			return fmt.Errorf("proto: QueryStorageProviderResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+			return fmt.Errorf("proto: QueryStorageProviderByOperatorAddressResponse: illegal tag %d (wire type %d)", fieldNum, wire)
 		}
 		switch fieldNum {
 		case 1:
diff --git a/x/sp/types/query.pb.gw.go b/x/sp/types/query.pb.gw.go
index df0f54170..727d403ec 100644
--- a/x/sp/types/query.pb.gw.go
+++ b/x/sp/types/query.pb.gw.go
@@ -228,15 +228,15 @@ func request_Query_StorageProvider_0(ctx context.Context, marshaler runtime.Mars
 		_   = err
 	)
 
-	val, ok = pathParams["spAddress"]
+	val, ok = pathParams["id"]
 	if !ok {
-		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "spAddress")
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
 	}
 
-	protoReq.SpAddress, err = runtime.String(val)
+	protoReq.Id, err = runtime.Uint32(val)
 
 	if err != nil {
-		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "spAddress", err)
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
 	}
 
 	msg, err := client.StorageProvider(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
@@ -255,15 +255,15 @@ func local_request_Query_StorageProvider_0(ctx context.Context, marshaler runtim
 		_   = err
 	)
 
-	val, ok = pathParams["spAddress"]
+	val, ok = pathParams["id"]
 	if !ok {
-		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "spAddress")
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
 	}
 
-	protoReq.SpAddress, err = runtime.String(val)
+	protoReq.Id, err = runtime.Uint32(val)
 
 	if err != nil {
-		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "spAddress", err)
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
 	}
 
 	msg, err := server.StorageProvider(ctx, &protoReq)
@@ -271,6 +271,42 @@ func local_request_Query_StorageProvider_0(ctx context.Context, marshaler runtim
 
 }
 
+var (
+	filter_Query_StorageProviderByOperatorAddress_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
+)
+
+func request_Query_StorageProviderByOperatorAddress_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryStorageProviderByOperatorAddressRequest
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_StorageProviderByOperatorAddress_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := client.StorageProviderByOperatorAddress(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_Query_StorageProviderByOperatorAddress_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryStorageProviderByOperatorAddressRequest
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_StorageProviderByOperatorAddress_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := server.StorageProviderByOperatorAddress(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
 // RegisterQueryHandlerServer registers the http handlers for service Query to "mux".
 // UnaryRPC     :call QueryServer directly.
 // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
@@ -392,6 +428,29 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv
 
 	})
 
+	mux.Handle("GET", pattern_Query_StorageProviderByOperatorAddress_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		var stream runtime.ServerTransportStream
+		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_Query_StorageProviderByOperatorAddress_0(rctx, inboundMarshaler, server, req, pathParams)
+		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_Query_StorageProviderByOperatorAddress_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
 	return nil
 }
 
@@ -533,6 +592,26 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie
 
 	})
 
+	mux.Handle("GET", pattern_Query_StorageProviderByOperatorAddress_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_Query_StorageProviderByOperatorAddress_0(rctx, inboundMarshaler, client, req, pathParams)
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_Query_StorageProviderByOperatorAddress_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
 	return nil
 }
 
@@ -545,7 +624,9 @@ var (
 
 	pattern_Query_QueryGetSecondarySpStorePriceByTime_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"greenfield", "sp", "get_secondary_sp_store_price_by_time", "timestamp"}, "", runtime.AssumeColonVerbOpt(false)))
 
-	pattern_Query_StorageProvider_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"greenfield", "storage_provider", "spAddress"}, "", runtime.AssumeColonVerbOpt(false)))
+	pattern_Query_StorageProvider_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"greenfield", "storage_provider", "id"}, "", runtime.AssumeColonVerbOpt(false)))
+
+	pattern_Query_StorageProviderByOperatorAddress_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"greenfield", "sp", "storage_provider_by_operator_address"}, "", runtime.AssumeColonVerbOpt(false)))
 )
 
 var (
@@ -558,4 +639,6 @@ var (
 	forward_Query_QueryGetSecondarySpStorePriceByTime_0 = runtime.ForwardResponseMessage
 
 	forward_Query_StorageProvider_0 = runtime.ForwardResponseMessage
+
+	forward_Query_StorageProviderByOperatorAddress_0 = runtime.ForwardResponseMessage
 )
diff --git a/x/sp/types/tx.pb.go b/x/sp/types/tx.pb.go
index ca4af3f73..493123fde 100644
--- a/x/sp/types/tx.pb.go
+++ b/x/sp/types/tx.pb.go
@@ -58,6 +58,9 @@ type MsgCreateStorageProvider struct {
 	FreeReadQuota uint64 `protobuf:"varint,11,opt,name=free_read_quota,json=freeReadQuota,proto3" json:"free_read_quota,omitempty"`
 	// store price, in bnb wei per charge byte
 	StorePrice github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,12,opt,name=store_price,json=storePrice,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"store_price"`
+	// bls_key defines the bls pub key of the Storage provider for sealing object
+	BlsKey   string `protobuf:"bytes,13,opt,name=bls_key,json=blsKey,proto3" json:"bls_key,omitempty"`
+	BlsProof string `protobuf:"bytes,14,opt,name=bls_proof,json=blsProof,proto3" json:"bls_proof,omitempty"`
 }
 
 func (m *MsgCreateStorageProvider) Reset()         { *m = MsgCreateStorageProvider{} }
@@ -163,6 +166,20 @@ func (m *MsgCreateStorageProvider) GetFreeReadQuota() uint64 {
 	return 0
 }
 
+func (m *MsgCreateStorageProvider) GetBlsKey() string {
+	if m != nil {
+		return m.BlsKey
+	}
+	return ""
+}
+
+func (m *MsgCreateStorageProvider) GetBlsProof() string {
+	if m != nil {
+		return m.BlsProof
+	}
+	return ""
+}
+
 // MsgCreateStorageProviderResponse defines the Msg/CreateStorageProvider response type.
 type MsgCreateStorageProviderResponse struct {
 }
@@ -302,6 +319,7 @@ func (m *MsgDepositResponse) XXX_DiscardUnknown() {
 var xxx_messageInfo_MsgDepositResponse proto.InternalMessageInfo
 
 // MsgEditStorageProvider defines a SDK message for editing an existing sp.
+// TODO: use sp id to edit the storage provider.
 type MsgEditStorageProvider struct {
 	SpAddress   string       `protobuf:"bytes,1,opt,name=sp_address,json=spAddress,proto3" json:"sp_address,omitempty"`
 	Endpoint    string       `protobuf:"bytes,2,opt,name=endpoint,proto3" json:"endpoint,omitempty"`
@@ -312,6 +330,9 @@ type MsgEditStorageProvider struct {
 	ApprovalAddress string `protobuf:"bytes,5,opt,name=approval_address,json=approvalAddress,proto3" json:"approval_address,omitempty"`
 	// gc_address defines one of the storage provider's accounts which is used for gc purpose
 	GcAddress string `protobuf:"bytes,6,opt,name=gc_address,json=gcAddress,proto3" json:"gc_address,omitempty"`
+	// bls_key defines the bls pub key of the Storage provider for sealing object
+	BlsKey   string `protobuf:"bytes,7,opt,name=bls_key,json=blsKey,proto3" json:"bls_key,omitempty"`
+	BlsProof string `protobuf:"bytes,8,opt,name=bls_proof,json=blsProof,proto3" json:"bls_proof,omitempty"`
 }
 
 func (m *MsgEditStorageProvider) Reset()         { *m = MsgEditStorageProvider{} }
@@ -389,6 +410,20 @@ func (m *MsgEditStorageProvider) GetGcAddress() string {
 	return ""
 }
 
+func (m *MsgEditStorageProvider) GetBlsKey() string {
+	if m != nil {
+		return m.BlsKey
+	}
+	return ""
+}
+
+func (m *MsgEditStorageProvider) GetBlsProof() string {
+	if m != nil {
+		return m.BlsProof
+	}
+	return ""
+}
+
 // MsgEditStorageProviderResponse defines the Msg/EditStorageProvider response type.
 type MsgEditStorageProviderResponse struct {
 }
@@ -631,60 +666,63 @@ func init() {
 func init() { proto.RegisterFile("greenfield/sp/tx.proto", fileDescriptor_f630c2933caa1bce) }
 
 var fileDescriptor_f630c2933caa1bce = []byte{
-	// 846 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xcf, 0x4e, 0xf3, 0x46,
-	0x10, 0x8f, 0x93, 0x7c, 0xc9, 0x97, 0x49, 0xbe, 0x2f, 0x95, 0x1b, 0xc0, 0xf1, 0xc1, 0x84, 0x48,
-	0x4d, 0x11, 0x52, 0x6c, 0x01, 0x52, 0x51, 0x29, 0x17, 0x42, 0xaa, 0x9e, 0x22, 0xd1, 0xa0, 0xf6,
-	0xd0, 0xaa, 0x8a, 0x1c, 0x7b, 0x31, 0x16, 0xc4, 0xbb, 0xec, 0x3a, 0x11, 0x5c, 0xfb, 0x00, 0x55,
-	0xd5, 0x27, 0xe9, 0x81, 0x27, 0xe8, 0xa5, 0x1c, 0x11, 0xa7, 0xaa, 0x07, 0x54, 0xc1, 0xa1, 0x6f,
-	0xd0, 0x5e, 0x2b, 0xdb, 0x6b, 0xc7, 0xce, 0x9f, 0x26, 0x05, 0x7a, 0x4a, 0xbc, 0xf3, 0xfb, 0xcd,
-	0x8c, 0x67, 0xe6, 0x37, 0x6b, 0x58, 0xb5, 0x28, 0x42, 0xce, 0xa9, 0x8d, 0x2e, 0x4c, 0x8d, 0x11,
-	0xcd, 0xbd, 0x52, 0x09, 0xc5, 0x2e, 0x16, 0xdf, 0x8d, 0xcf, 0x55, 0x46, 0x64, 0xc5, 0xc0, 0x6c,
-	0x80, 0x99, 0xd6, 0xd7, 0x19, 0xd2, 0x46, 0xdb, 0x7d, 0xe4, 0xea, 0xdb, 0x9a, 0x81, 0x6d, 0x27,
-	0x80, 0xcb, 0x6b, 0xdc, 0x3e, 0x60, 0x96, 0x36, 0xda, 0xf6, 0x7e, 0xb8, 0xa1, 0x1a, 0x18, 0x7a,
-	0xfe, 0x93, 0x16, 0x3c, 0x70, 0x53, 0xc5, 0xc2, 0x16, 0x0e, 0xce, 0xbd, 0x7f, 0xfc, 0x54, 0x4e,
-	0x26, 0x44, 0x74, 0xaa, 0x0f, 0x42, 0x46, 0x75, 0x22, 0xd9, 0x6b, 0x82, 0xb8, 0xa9, 0xfe, 0x53,
-	0x0e, 0xa4, 0x0e, 0xb3, 0x8e, 0x28, 0xd2, 0x5d, 0x74, 0xe2, 0x62, 0xaa, 0x5b, 0xe8, 0x98, 0xe2,
-	0x91, 0x6d, 0x22, 0x2a, 0xee, 0x40, 0xde, 0xf0, 0x0c, 0x98, 0x4a, 0x42, 0x4d, 0xd8, 0x2c, 0xb4,
-	0xa4, 0xfb, 0x9b, 0x66, 0x85, 0x27, 0x73, 0x68, 0x9a, 0x14, 0x31, 0x76, 0xe2, 0x52, 0xdb, 0xb1,
-	0xba, 0x21, 0x50, 0x6c, 0x41, 0xd1, 0x44, 0xcc, 0xa0, 0x36, 0x71, 0x6d, 0xec, 0x48, 0xe9, 0x9a,
-	0xb0, 0x59, 0xdc, 0x91, 0xd5, 0x44, 0x59, 0xd4, 0xf6, 0x18, 0xd1, 0xca, 0xde, 0x3e, 0xac, 0xa7,
-	0xba, 0x71, 0x92, 0xb8, 0x07, 0xc0, 0x48, 0x4f, 0x0f, 0x02, 0x48, 0x99, 0x05, 0xa1, 0x0b, 0x8c,
-	0xf0, 0x03, 0xf1, 0x10, 0xca, 0xa7, 0x43, 0xc7, 0xb4, 0x1d, 0x2b, 0x62, 0x67, 0x17, 0xb0, 0xdf,
-	0x73, 0x42, 0xe8, 0xe2, 0x33, 0x28, 0x31, 0xa4, 0x5f, 0x44, 0xfc, 0x37, 0x0b, 0xf8, 0x45, 0x0f,
-	0x1d, 0x92, 0x8f, 0xe0, 0x03, 0x9d, 0x10, 0x8a, 0x47, 0x31, 0x07, 0xb9, 0x05, 0x0e, 0xca, 0x21,
-	0x23, 0x74, 0xb2, 0x07, 0x60, 0x19, 0x11, 0x3d, 0xbf, 0xe8, 0xed, 0x2d, 0x23, 0x24, 0xca, 0xf0,
-	0x16, 0x39, 0x26, 0xc1, 0xb6, 0xe3, 0x4a, 0x6f, 0x3d, 0x5a, 0x37, 0x7a, 0x16, 0x3f, 0x85, 0xbc,
-	0x89, 0x08, 0x66, 0xb6, 0x2b, 0x15, 0xfc, 0x96, 0x54, 0x55, 0xee, 0xce, 0x1b, 0x4d, 0x95, 0x8f,
-	0xa6, 0x7a, 0x84, 0xed, 0xb0, 0x23, 0x21, 0x5e, 0xfc, 0x16, 0x80, 0x22, 0xdd, 0xec, 0x11, 0x6a,
-	0x1b, 0x48, 0x02, 0x3f, 0x9f, 0x03, 0x0f, 0xf2, 0xfb, 0xc3, 0x7a, 0xc3, 0xb2, 0xdd, 0xb3, 0x61,
-	0x5f, 0x35, 0xf0, 0x80, 0x0f, 0x29, 0xff, 0x69, 0x32, 0xf3, 0x9c, 0x0f, 0x5a, 0x1b, 0x19, 0xf7,
-	0x37, 0x4d, 0xe0, 0xe1, 0xda, 0xc8, 0xe8, 0x16, 0x3c, 0x7f, 0xc7, 0x9e, 0x3b, 0xb1, 0x01, 0xe5,
-	0x53, 0x8a, 0x50, 0xcf, 0x8f, 0x70, 0x39, 0xc4, 0xae, 0x2e, 0x15, 0x6b, 0xc2, 0x66, 0xb6, 0xfb,
-	0xce, 0x3b, 0xee, 0x22, 0xdd, 0xfc, 0xd2, 0x3b, 0x14, 0xbf, 0x83, 0x22, 0x73, 0x31, 0x45, 0x3c,
-	0x8b, 0xd2, 0x2b, 0x64, 0x01, 0xbe, 0x43, 0x3f, 0x8d, 0xfd, 0xd2, 0xf7, 0x7f, 0xfe, 0xbc, 0x15,
-	0xce, 0x70, 0xbd, 0x0e, 0xb5, 0x79, 0x9a, 0xe8, 0x22, 0x46, 0xb0, 0xc3, 0x50, 0xfd, 0x17, 0x01,
-	0xa0, 0xc3, 0xac, 0x36, 0x2f, 0xd2, 0x73, 0xa4, 0x92, 0x1c, 0xf3, 0xf4, 0xf2, 0x63, 0x1e, 0x6b,
-	0x66, 0xe6, 0xbf, 0x35, 0x73, 0xe2, 0x45, 0x2b, 0x20, 0x8e, 0xdf, 0x21, 0x7a, 0xb5, 0xbf, 0xd3,
-	0xb0, 0xda, 0x61, 0xd6, 0xe7, 0xa6, 0xed, 0x4e, 0x6e, 0x84, 0x64, 0xca, 0xc2, 0xf2, 0x29, 0xc7,
-	0x67, 0x33, 0x3d, 0x31, 0x9b, 0x07, 0xc9, 0x95, 0x91, 0x59, 0xb4, 0x32, 0x92, 0xcb, 0x62, 0x52,
-	0xb0, 0xd9, 0x97, 0x0a, 0xf6, 0xcd, 0xcb, 0x04, 0x9b, 0x5b, 0x5a, 0xb0, 0xfb, 0x65, 0xaf, 0x19,
-	0xb1, 0x82, 0xd6, 0x6b, 0xa0, 0xcc, 0x2e, 0x7c, 0xd4, 0x9b, 0x5f, 0xd3, 0xb0, 0xd6, 0x61, 0xd6,
-	0x57, 0xc4, 0xf4, 0x66, 0x93, 0x44, 0x30, 0x4f, 0x4b, 0xcf, 0x6e, 0x4e, 0x52, 0xe1, 0xe9, 0xff,
-	0x5d, 0xe1, 0x99, 0x25, 0x14, 0x9e, 0x7d, 0x65, 0x85, 0x4f, 0xd5, 0x7a, 0x03, 0xd6, 0xe7, 0x14,
-	0x32, 0x2a, 0xf6, 0x0f, 0x02, 0x94, 0x23, 0xcc, 0xb1, 0x7f, 0xa3, 0x8a, 0x9f, 0x40, 0x41, 0x1f,
-	0xba, 0x67, 0x98, 0xda, 0xee, 0xf5, 0xe2, 0x1a, 0x47, 0x50, 0x71, 0x17, 0x72, 0xc1, 0x9d, 0xcc,
-	0xaf, 0xc4, 0x95, 0x89, 0xf9, 0x0e, 0xdc, 0x73, 0xb9, 0x72, 0xe8, 0xfe, 0x7b, 0x2f, 0xe9, 0xb1,
-	0x93, 0x7a, 0x35, 0xd6, 0xfc, 0x80, 0x10, 0xe6, 0xba, 0xf3, 0x57, 0x06, 0x32, 0x1d, 0x66, 0x89,
-	0x97, 0xb0, 0x32, 0xfb, 0x32, 0xff, 0x78, 0x22, 0xe0, 0xbc, 0x0d, 0x27, 0x6b, 0x4b, 0x02, 0xc3,
-	0xd0, 0xe2, 0x17, 0x90, 0x0f, 0xd7, 0x60, 0x75, 0x9a, 0xcb, 0x4d, 0xf2, 0xc6, 0x5c, 0x53, 0xe4,
-	0xe8, 0x1c, 0x3e, 0x9c, 0xb5, 0x74, 0x3e, 0x9a, 0x66, 0xce, 0x80, 0xc9, 0xcd, 0xa5, 0x60, 0x51,
-	0x30, 0x07, 0x2a, 0x33, 0x55, 0xd4, 0x98, 0x76, 0x33, 0x0b, 0x27, 0xab, 0xcb, 0xe1, 0xa2, 0x78,
-	0x5f, 0x43, 0x29, 0x31, 0x48, 0xca, 0x3c, 0x7e, 0x60, 0x97, 0x1b, 0xff, 0x6e, 0x0f, 0xfd, 0xb6,
-	0xda, 0xb7, 0x8f, 0x8a, 0x70, 0xf7, 0xa8, 0x08, 0x7f, 0x3c, 0x2a, 0xc2, 0x8f, 0x4f, 0x4a, 0xea,
-	0xee, 0x49, 0x49, 0xfd, 0xf6, 0xa4, 0xa4, 0xbe, 0xd9, 0x8a, 0x89, 0xa6, 0xef, 0xf4, 0x9b, 0xc6,
-	0x99, 0x6e, 0x3b, 0x5a, 0xec, 0x5b, 0xf0, 0x2a, 0xfa, 0x1a, 0xec, 0xe7, 0xfc, 0xcf, 0xc1, 0xdd,
-	0x7f, 0x02, 0x00, 0x00, 0xff, 0xff, 0xca, 0x99, 0x69, 0xbd, 0xd8, 0x0a, 0x00, 0x00,
+	// 894 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xcd, 0x6e, 0xdb, 0x46,
+	0x10, 0x36, 0x25, 0x45, 0xb2, 0x46, 0xb6, 0x55, 0xb0, 0x4e, 0x4c, 0xb1, 0x00, 0xad, 0x08, 0xa8,
+	0x6b, 0x04, 0x10, 0x09, 0x3b, 0x40, 0x83, 0xa6, 0xb9, 0xc4, 0x56, 0xd1, 0x43, 0x21, 0x40, 0x55,
+	0xd0, 0x1e, 0x5a, 0x14, 0x02, 0x7f, 0x56, 0x34, 0x61, 0x89, 0xbb, 0xd9, 0xa5, 0x84, 0xe8, 0xda,
+	0x07, 0x28, 0xfa, 0x22, 0x05, 0x7a, 0xc8, 0x13, 0xf4, 0x52, 0x1f, 0x83, 0x9c, 0x8a, 0x1e, 0x82,
+	0xc2, 0x3e, 0xf4, 0x0d, 0x7a, 0x2e, 0x96, 0x5c, 0x52, 0x24, 0x25, 0x55, 0x6a, 0xe2, 0x9c, 0x24,
+	0xee, 0x7c, 0xdf, 0xcc, 0x70, 0xe6, 0x9b, 0xe1, 0xc2, 0x3d, 0x97, 0x22, 0xe4, 0x0f, 0x3d, 0x34,
+	0x72, 0x0c, 0x46, 0x8c, 0xe0, 0x85, 0x4e, 0x28, 0x0e, 0xb0, 0xbc, 0x3b, 0x3f, 0xd7, 0x19, 0x51,
+	0x35, 0x1b, 0xb3, 0x31, 0x66, 0x86, 0x65, 0x32, 0x64, 0x4c, 0x4f, 0x2c, 0x14, 0x98, 0x27, 0x86,
+	0x8d, 0x3d, 0x3f, 0x82, 0xab, 0x07, 0xc2, 0x3e, 0x66, 0xae, 0x31, 0x3d, 0xe1, 0x3f, 0xc2, 0xd0,
+	0x88, 0x0c, 0x83, 0xf0, 0xc9, 0x88, 0x1e, 0x84, 0x69, 0xdf, 0xc5, 0x2e, 0x8e, 0xce, 0xf9, 0x3f,
+	0x71, 0xaa, 0x66, 0x13, 0x22, 0x26, 0x35, 0xc7, 0x31, 0xa3, 0x91, 0x4b, 0x76, 0x46, 0x90, 0x30,
+	0xb5, 0xae, 0xca, 0xa0, 0x74, 0x99, 0x7b, 0x4e, 0x91, 0x19, 0xa0, 0x67, 0x01, 0xa6, 0xa6, 0x8b,
+	0x7a, 0x14, 0x4f, 0x3d, 0x07, 0x51, 0xf9, 0x14, 0x2a, 0x36, 0x37, 0x60, 0xaa, 0x48, 0x4d, 0xe9,
+	0xb8, 0x7a, 0xa6, 0xbc, 0x7e, 0xd9, 0xde, 0x17, 0xc9, 0x3c, 0x75, 0x1c, 0x8a, 0x18, 0x7b, 0x16,
+	0x50, 0xcf, 0x77, 0xfb, 0x31, 0x50, 0x3e, 0x83, 0x9a, 0x83, 0x98, 0x4d, 0x3d, 0x12, 0x78, 0xd8,
+	0x57, 0x0a, 0x4d, 0xe9, 0xb8, 0x76, 0xaa, 0xea, 0x99, 0xb2, 0xe8, 0x9d, 0x39, 0xe2, 0xac, 0x74,
+	0xf5, 0xe6, 0x70, 0xab, 0x9f, 0x26, 0xc9, 0x8f, 0x00, 0x18, 0x19, 0x98, 0x51, 0x00, 0xa5, 0xb8,
+	0x26, 0x74, 0x95, 0x11, 0x71, 0x20, 0x3f, 0x85, 0xfa, 0x70, 0xe2, 0x3b, 0x9e, 0xef, 0x26, 0xec,
+	0xd2, 0x1a, 0xf6, 0x9e, 0x20, 0xc4, 0x2e, 0x3e, 0x87, 0x1d, 0x86, 0xcc, 0x51, 0xc2, 0xbf, 0xb3,
+	0x86, 0x5f, 0xe3, 0xe8, 0x98, 0x7c, 0x0e, 0x1f, 0x98, 0x84, 0x50, 0x3c, 0x4d, 0x39, 0x28, 0xaf,
+	0x71, 0x50, 0x8f, 0x19, 0xb1, 0x93, 0x47, 0x00, 0xae, 0x9d, 0xd0, 0x2b, 0xeb, 0xde, 0xde, 0xb5,
+	0x63, 0xa2, 0x0a, 0xdb, 0xc8, 0x77, 0x08, 0xf6, 0xfc, 0x40, 0xd9, 0xe6, 0xb4, 0x7e, 0xf2, 0x2c,
+	0x7f, 0x06, 0x15, 0x07, 0x11, 0xcc, 0xbc, 0x40, 0xa9, 0x86, 0x2d, 0x69, 0xe8, 0xc2, 0x1d, 0x97,
+	0xa6, 0x2e, 0xa4, 0xa9, 0x9f, 0x63, 0x2f, 0xee, 0x48, 0x8c, 0x97, 0xbf, 0x07, 0xa0, 0xc8, 0x74,
+	0x06, 0x84, 0x7a, 0x36, 0x52, 0x20, 0xcc, 0xe7, 0x09, 0x87, 0xfc, 0xf9, 0xe6, 0xf0, 0xc8, 0xf5,
+	0x82, 0x8b, 0x89, 0xa5, 0xdb, 0x78, 0x2c, 0x44, 0x2a, 0x7e, 0xda, 0xcc, 0xb9, 0x14, 0x42, 0xeb,
+	0x20, 0xfb, 0xf5, 0xcb, 0x36, 0x88, 0x70, 0x1d, 0x64, 0xf7, 0xab, 0xdc, 0x5f, 0x8f, 0xbb, 0x93,
+	0x8f, 0xa0, 0x3e, 0xa4, 0x08, 0x0d, 0xc2, 0x08, 0xcf, 0x27, 0x38, 0x30, 0x95, 0x5a, 0x53, 0x3a,
+	0x2e, 0xf5, 0x77, 0xf9, 0x71, 0x1f, 0x99, 0xce, 0xd7, 0xfc, 0x50, 0xfe, 0x01, 0x6a, 0x2c, 0xc0,
+	0x14, 0x89, 0x2c, 0x76, 0x6e, 0x21, 0x0b, 0x08, 0x1d, 0x46, 0x69, 0x1c, 0x40, 0xc5, 0x1a, 0xb1,
+	0xc1, 0x25, 0x9a, 0x29, 0xbb, 0x61, 0xe5, 0xca, 0xd6, 0x88, 0x7d, 0x85, 0x66, 0xf2, 0x47, 0x50,
+	0xe5, 0x06, 0x42, 0x31, 0x1e, 0x2a, 0x7b, 0x51, 0x51, 0xad, 0x11, 0xeb, 0xf1, 0xe7, 0xc7, 0x3b,
+	0x3f, 0xfe, 0xfd, 0xeb, 0x83, 0x58, 0xf9, 0xad, 0x16, 0x34, 0x57, 0x4d, 0x52, 0x1f, 0x31, 0x82,
+	0x7d, 0x86, 0x5a, 0xbf, 0x49, 0x00, 0x5d, 0xe6, 0x76, 0x44, 0x69, 0xdf, 0x66, 0xc0, 0xb2, 0xc3,
+	0x51, 0xd8, 0x7c, 0x38, 0x52, 0x12, 0x28, 0xfe, 0x3f, 0x09, 0xe4, 0x5e, 0x74, 0x1f, 0xe4, 0xf9,
+	0x3b, 0x24, 0xaf, 0xf6, 0x4b, 0x11, 0xee, 0x75, 0x99, 0xfb, 0x85, 0xe3, 0x05, 0xf9, 0x3d, 0x92,
+	0x4d, 0x59, 0xda, 0x3c, 0xe5, 0xb4, 0xa2, 0x0b, 0x39, 0x45, 0x3f, 0xc9, 0x2e, 0x9a, 0xe2, 0xba,
+	0x45, 0x93, 0x5d, 0x31, 0xf9, 0x31, 0x2f, 0xbd, 0xeb, 0x98, 0xdf, 0x79, 0xb7, 0x31, 0x2f, 0x6f,
+	0x3e, 0xe6, 0x29, 0xad, 0x56, 0x56, 0x6b, 0x75, 0x3b, 0xa7, 0xd5, 0x3a, 0x6f, 0x61, 0xaa, 0x0d,
+	0xad, 0x26, 0x68, 0xcb, 0xdb, 0x95, 0x74, 0xf4, 0xf7, 0x02, 0x1c, 0x74, 0x99, 0xfb, 0x0d, 0x71,
+	0xb8, 0xa2, 0x49, 0x02, 0xe3, 0x03, 0xf3, 0xd6, 0x2d, 0xcd, 0x6e, 0x93, 0xc2, 0x7b, 0xdf, 0x26,
+	0xc5, 0x0d, 0xb6, 0x49, 0xe9, 0x76, 0xb7, 0xc9, 0x62, 0xad, 0xef, 0xc3, 0xe1, 0x8a, 0x42, 0x26,
+	0xc5, 0xfe, 0x49, 0x82, 0x7a, 0x82, 0xe9, 0x85, 0x5f, 0x6f, 0xf9, 0x53, 0xa8, 0x9a, 0x93, 0xe0,
+	0x02, 0x53, 0x2f, 0x98, 0xad, 0xaf, 0x71, 0x02, 0x95, 0x1f, 0x42, 0x39, 0xfa, 0xfe, 0x8b, 0xcf,
+	0xef, 0xdd, 0xdc, 0x54, 0x44, 0xee, 0xc5, 0x90, 0x0b, 0xe8, 0xe3, 0x3d, 0x9e, 0xf4, 0xdc, 0x49,
+	0xab, 0x91, 0x6a, 0x7e, 0x44, 0x88, 0x73, 0x3d, 0xfd, 0xa7, 0x08, 0xc5, 0x2e, 0x73, 0xe5, 0xe7,
+	0x70, 0x77, 0xf9, 0xc5, 0xe1, 0x93, 0x5c, 0xc0, 0x55, 0x7b, 0x51, 0x35, 0x36, 0x04, 0xc6, 0xa1,
+	0xe5, 0x2f, 0xa1, 0x12, 0x2f, 0xcf, 0xc6, 0x22, 0x57, 0x98, 0xd4, 0xfb, 0x2b, 0x4d, 0x89, 0xa3,
+	0x4b, 0xf8, 0x70, 0xd9, 0xaa, 0xfa, 0x78, 0x91, 0xb9, 0x04, 0xa6, 0xb6, 0x37, 0x82, 0x25, 0xc1,
+	0x7c, 0xd8, 0x5f, 0x3a, 0x45, 0x47, 0x8b, 0x6e, 0x96, 0xe1, 0x54, 0x7d, 0x33, 0x5c, 0x12, 0xef,
+	0x5b, 0xd8, 0xc9, 0x08, 0x49, 0x5b, 0xc5, 0x8f, 0xec, 0xea, 0xd1, 0x7f, 0xdb, 0x63, 0xbf, 0x67,
+	0x9d, 0xab, 0x6b, 0x4d, 0x7a, 0x75, 0xad, 0x49, 0x7f, 0x5d, 0x6b, 0xd2, 0xcf, 0x37, 0xda, 0xd6,
+	0xab, 0x1b, 0x6d, 0xeb, 0x8f, 0x1b, 0x6d, 0xeb, 0xbb, 0x07, 0xa9, 0xa1, 0xb1, 0x7c, 0xab, 0x6d,
+	0x5f, 0x98, 0x9e, 0x6f, 0xa4, 0xee, 0x9d, 0x2f, 0x92, 0x9b, 0xa7, 0x55, 0x0e, 0xaf, 0x9e, 0x0f,
+	0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xeb, 0x83, 0x53, 0x9f, 0x44, 0x0b, 0x00, 0x00,
 }
 
 // Reference imports to suppress errors if they are not otherwise used.
@@ -939,6 +977,20 @@ func (m *MsgCreateStorageProvider) MarshalToSizedBuffer(dAtA []byte) (int, error
 	_ = i
 	var l int
 	_ = l
+	if len(m.BlsProof) > 0 {
+		i -= len(m.BlsProof)
+		copy(dAtA[i:], m.BlsProof)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.BlsProof)))
+		i--
+		dAtA[i] = 0x72
+	}
+	if len(m.BlsKey) > 0 {
+		i -= len(m.BlsKey)
+		copy(dAtA[i:], m.BlsKey)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.BlsKey)))
+		i--
+		dAtA[i] = 0x6a
+	}
 	{
 		size := m.StorePrice.Size()
 		i -= size
@@ -1149,6 +1201,20 @@ func (m *MsgEditStorageProvider) MarshalToSizedBuffer(dAtA []byte) (int, error)
 	_ = i
 	var l int
 	_ = l
+	if len(m.BlsProof) > 0 {
+		i -= len(m.BlsProof)
+		copy(dAtA[i:], m.BlsProof)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.BlsProof)))
+		i--
+		dAtA[i] = 0x42
+	}
+	if len(m.BlsKey) > 0 {
+		i -= len(m.BlsKey)
+		copy(dAtA[i:], m.BlsKey)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.BlsKey)))
+		i--
+		dAtA[i] = 0x3a
+	}
 	if len(m.GcAddress) > 0 {
 		i -= len(m.GcAddress)
 		copy(dAtA[i:], m.GcAddress)
@@ -1419,6 +1485,14 @@ func (m *MsgCreateStorageProvider) Size() (n int) {
 	}
 	l = m.StorePrice.Size()
 	n += 1 + l + sovTx(uint64(l))
+	l = len(m.BlsKey)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	l = len(m.BlsProof)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
 	return n
 }
 
@@ -1489,6 +1563,14 @@ func (m *MsgEditStorageProvider) Size() (n int) {
 	if l > 0 {
 		n += 1 + l + sovTx(uint64(l))
 	}
+	l = len(m.BlsKey)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	l = len(m.BlsProof)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
 	return n
 }
 
@@ -1966,6 +2048,70 @@ func (m *MsgCreateStorageProvider) Unmarshal(dAtA []byte) error {
 				return err
 			}
 			iNdEx = postIndex
+		case 13:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field BlsKey", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.BlsKey = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 14:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field BlsProof", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.BlsProof = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipTx(dAtA[iNdEx:])
@@ -2459,6 +2605,70 @@ func (m *MsgEditStorageProvider) Unmarshal(dAtA []byte) error {
 			}
 			m.GcAddress = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
+		case 7:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field BlsKey", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.BlsKey = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 8:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field BlsProof", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.BlsProof = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipTx(dAtA[iNdEx:])
diff --git a/x/sp/types/types.go b/x/sp/types/types.go
index 6011876fb..8e1a56829 100644
--- a/x/sp/types/types.go
+++ b/x/sp/types/types.go
@@ -1,6 +1,8 @@
 package types
 
 import (
+	"encoding/hex"
+
 	"cosmossdk.io/errors"
 	"cosmossdk.io/math"
 	sdk "github.com/cosmos/cosmos-sdk/types"
@@ -16,11 +18,19 @@ const (
 
 // NewStorageProvider constructs a new StorageProvider
 func NewStorageProvider(
-	operator sdk.AccAddress, fundingAddress sdk.AccAddress,
+	spID uint32, operator sdk.AccAddress, fundingAddress sdk.AccAddress,
 	sealAddress sdk.AccAddress, approvalAddress sdk.AccAddress, gcAddress sdk.AccAddress,
 	totalDeposit math.Int, endpoint string,
-	description Description) (StorageProvider, error) {
+	description Description,
+	blsKey string) (StorageProvider, error) {
+
+	blsKeyBytes, err := hex.DecodeString(blsKey)
+	if err != nil {
+		return StorageProvider{}, err
+	}
+
 	return StorageProvider{
+		Id:              spID,
 		OperatorAddress: operator.String(),
 		FundingAddress:  fundingAddress.String(),
 		SealAddress:     sealAddress.String(),
@@ -29,10 +39,11 @@ func NewStorageProvider(
 		TotalDeposit:    totalDeposit,
 		Endpoint:        endpoint,
 		Description:     description,
+		BlsKey:          blsKeyBytes,
 	}, nil
 }
 
-func (sp *StorageProvider) GetOperator() sdk.AccAddress {
+func (sp *StorageProvider) GetOperatorAccAddress() sdk.AccAddress {
 	addr := sdk.MustAccAddressFromHex(sp.OperatorAddress)
 	return addr
 }
@@ -131,7 +142,3 @@ func (d *Description) UpdateDescription(d2 *Description) (*Description, error) {
 
 	return d2, nil
 }
-
-func (s *SpStoragePrice) GetSpAccAddress() sdk.AccAddress {
-	return sdk.MustAccAddressFromHex(s.SpAddress)
-}
diff --git a/x/sp/types/types.pb.go b/x/sp/types/types.pb.go
index c642ad968..b6a7d9fa4 100644
--- a/x/sp/types/types.pb.go
+++ b/x/sp/types/types.pb.go
@@ -142,24 +142,28 @@ func (m *Description) GetDetails() string {
 
 // StorageProvider defines the meta info of storage provider
 type StorageProvider struct {
+	// // id is the identifier of the storage provider, used in virtual group
+	Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
 	// operator_address defines the account address of the storage provider's operator; It also is the unique index key of sp.
-	OperatorAddress string `protobuf:"bytes,1,opt,name=operator_address,json=operatorAddress,proto3" json:"operator_address,omitempty"`
+	OperatorAddress string `protobuf:"bytes,2,opt,name=operator_address,json=operatorAddress,proto3" json:"operator_address,omitempty"`
 	// funding_address defines one of the storage provider's accounts which is used to deposit and reward.
-	FundingAddress string `protobuf:"bytes,2,opt,name=funding_address,json=fundingAddress,proto3" json:"funding_address,omitempty"`
+	FundingAddress string `protobuf:"bytes,3,opt,name=funding_address,json=fundingAddress,proto3" json:"funding_address,omitempty"`
 	// seal_address defines one of the storage provider's accounts which is used to SealObject
-	SealAddress string `protobuf:"bytes,3,opt,name=seal_address,json=sealAddress,proto3" json:"seal_address,omitempty"`
+	SealAddress string `protobuf:"bytes,4,opt,name=seal_address,json=sealAddress,proto3" json:"seal_address,omitempty"`
 	// approval_address defines one of the storage provider's accounts which is used to approve use's createBucket/createObject request
-	ApprovalAddress string `protobuf:"bytes,4,opt,name=approval_address,json=approvalAddress,proto3" json:"approval_address,omitempty"`
+	ApprovalAddress string `protobuf:"bytes,5,opt,name=approval_address,json=approvalAddress,proto3" json:"approval_address,omitempty"`
 	// gc_address defines one of the storage provider's accounts which is used for gc purpose.
-	GcAddress string `protobuf:"bytes,5,opt,name=gc_address,json=gcAddress,proto3" json:"gc_address,omitempty"`
+	GcAddress string `protobuf:"bytes,6,opt,name=gc_address,json=gcAddress,proto3" json:"gc_address,omitempty"`
 	// total_deposit defines the number of tokens deposited by this storage provider for staking.
-	TotalDeposit github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,6,opt,name=total_deposit,json=totalDeposit,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"total_deposit"`
+	TotalDeposit github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,7,opt,name=total_deposit,json=totalDeposit,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"total_deposit"`
 	// status defines the current service status of this storage provider
-	Status Status `protobuf:"varint,7,opt,name=status,proto3,enum=greenfield.sp.Status" json:"status,omitempty"`
+	Status Status `protobuf:"varint,8,opt,name=status,proto3,enum=greenfield.sp.Status" json:"status,omitempty"`
 	// endpoint define the storage provider's network service address
-	Endpoint string `protobuf:"bytes,8,opt,name=endpoint,proto3" json:"endpoint,omitempty"`
+	Endpoint string `protobuf:"bytes,9,opt,name=endpoint,proto3" json:"endpoint,omitempty"`
 	// description defines the description terms for the storage provider.
-	Description Description `protobuf:"bytes,9,opt,name=description,proto3" json:"description"`
+	Description Description `protobuf:"bytes,10,opt,name=description,proto3" json:"description"`
+	// bls_key defines the bls pub key of the Storage provider for sealing object and completing migration
+	BlsKey []byte `protobuf:"bytes,11,opt,name=bls_key,json=blsKey,proto3" json:"bls_key,omitempty"`
 }
 
 func (m *StorageProvider) Reset()         { *m = StorageProvider{} }
@@ -195,6 +199,13 @@ func (m *StorageProvider) XXX_DiscardUnknown() {
 
 var xxx_messageInfo_StorageProvider proto.InternalMessageInfo
 
+func (m *StorageProvider) GetId() uint32 {
+	if m != nil {
+		return m.Id
+	}
+	return 0
+}
+
 func (m *StorageProvider) GetOperatorAddress() string {
 	if m != nil {
 		return m.OperatorAddress
@@ -251,6 +262,13 @@ func (m *StorageProvider) GetDescription() Description {
 	return Description{}
 }
 
+func (m *StorageProvider) GetBlsKey() []byte {
+	if m != nil {
+		return m.BlsKey
+	}
+	return nil
+}
+
 type RewardInfo struct {
 	Address string     `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
 	Amount  types.Coin `protobuf:"bytes,2,opt,name=amount,proto3" json:"amount"`
@@ -305,8 +323,8 @@ func (m *RewardInfo) GetAmount() types.Coin {
 
 // storage price of a specific sp
 type SpStoragePrice struct {
-	// sp address
-	SpAddress string `protobuf:"bytes,1,opt,name=sp_address,json=spAddress,proto3" json:"sp_address,omitempty"`
+	// sp id
+	SpId uint32 `protobuf:"varint,1,opt,name=sp_id,json=spId,proto3" json:"sp_id,omitempty"`
 	// update time, unix timestamp in seconds
 	UpdateTimeSec int64 `protobuf:"varint,2,opt,name=update_time_sec,json=updateTimeSec,proto3" json:"update_time_sec,omitempty"`
 	// read price, in bnb wei per charge byte
@@ -350,11 +368,11 @@ func (m *SpStoragePrice) XXX_DiscardUnknown() {
 
 var xxx_messageInfo_SpStoragePrice proto.InternalMessageInfo
 
-func (m *SpStoragePrice) GetSpAddress() string {
+func (m *SpStoragePrice) GetSpId() uint32 {
 	if m != nil {
-		return m.SpAddress
+		return m.SpId
 	}
-	return ""
+	return 0
 }
 
 func (m *SpStoragePrice) GetUpdateTimeSec() int64 {
@@ -431,57 +449,60 @@ func init() {
 func init() { proto.RegisterFile("greenfield/sp/types.proto", fileDescriptor_7a9af9b5be8c2eeb) }
 
 var fileDescriptor_7a9af9b5be8c2eeb = []byte{
-	// 799 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0xd1, 0x6a, 0x1b, 0x47,
-	0x14, 0xd5, 0xca, 0xb2, 0x1c, 0x5d, 0xc5, 0x96, 0xba, 0xd8, 0x74, 0xad, 0x82, 0x62, 0xf4, 0x60,
-	0xdc, 0x80, 0x24, 0xe2, 0x3e, 0xe4, 0xa1, 0x7d, 0x91, 0x25, 0x25, 0xa8, 0x84, 0xa4, 0xdd, 0x95,
-	0x4b, 0x69, 0x29, 0xcb, 0x68, 0xe7, 0x7a, 0x33, 0xc4, 0x9a, 0xd9, 0xce, 0x8c, 0x9c, 0xea, 0x0f,
-	0xfa, 0xd8, 0x2f, 0xe8, 0x43, 0xfb, 0x0b, 0xf9, 0x86, 0x92, 0xc7, 0x10, 0x28, 0x94, 0x3e, 0x84,
-	0x62, 0xff, 0x48, 0xd9, 0x9d, 0xd9, 0xb5, 0x6a, 0x0a, 0x4a, 0xc0, 0x4f, 0xd2, 0x3d, 0xf7, 0x9e,
-	0x33, 0x67, 0x66, 0xce, 0x32, 0xb0, 0x1f, 0x4b, 0x44, 0x7e, 0xc6, 0xf0, 0x9c, 0xf6, 0x55, 0xd2,
-	0xd7, 0xcb, 0x04, 0x55, 0x2f, 0x91, 0x42, 0x0b, 0x77, 0xfb, 0xba, 0xd5, 0x53, 0x49, 0xab, 0x1d,
-	0x09, 0x35, 0x17, 0xaa, 0x3f, 0x23, 0x0a, 0xfb, 0x17, 0x0f, 0x66, 0xa8, 0xc9, 0x83, 0x7e, 0x24,
-	0x18, 0x37, 0xe3, 0xad, 0x7d, 0xd3, 0x0f, 0xb3, 0xaa, 0x6f, 0x0a, 0xdb, 0xda, 0x8d, 0x45, 0x2c,
-	0x0c, 0x9e, 0xfe, 0x33, 0x68, 0xe7, 0x37, 0x07, 0xea, 0x23, 0x54, 0x91, 0x64, 0x89, 0x66, 0x82,
-	0xbb, 0x1e, 0x6c, 0xcd, 0x05, 0x67, 0x2f, 0x50, 0x7a, 0xce, 0x81, 0x73, 0x54, 0xf3, 0xf3, 0xd2,
-	0x6d, 0xc1, 0x1d, 0x46, 0x91, 0x6b, 0xa6, 0x97, 0x5e, 0x39, 0x6b, 0x15, 0x75, 0xca, 0x7a, 0x89,
-	0x33, 0xc5, 0x34, 0x7a, 0x1b, 0x86, 0x65, 0x4b, 0xf7, 0x53, 0x68, 0x2a, 0x8c, 0x16, 0x92, 0xe9,
-	0x65, 0x18, 0x09, 0xae, 0x49, 0xa4, 0xbd, 0x4a, 0x36, 0xd2, 0xc8, 0xf1, 0xa1, 0x81, 0x53, 0x11,
-	0x8a, 0x9a, 0xb0, 0x73, 0xe5, 0x6d, 0x1a, 0x11, 0x5b, 0x76, 0xfe, 0xa8, 0x40, 0x23, 0xd0, 0x42,
-	0x92, 0x18, 0xbf, 0x92, 0xe2, 0x82, 0x51, 0x94, 0xee, 0x10, 0x9a, 0x22, 0x41, 0x49, 0xb4, 0x90,
-	0x21, 0xa1, 0x54, 0xa2, 0x52, 0xc6, 0xf1, 0x89, 0xf7, 0xf6, 0x55, 0x77, 0xd7, 0x6e, 0x7d, 0x60,
-	0x3a, 0x81, 0x96, 0x8c, 0xc7, 0x7e, 0x23, 0x67, 0x58, 0xd8, 0x1d, 0x40, 0xe3, 0x6c, 0xc1, 0x29,
-	0xe3, 0x71, 0xa1, 0x51, 0x5e, 0xa3, 0xb1, 0x63, 0x09, 0xb9, 0xc4, 0xe7, 0x70, 0x57, 0x21, 0x39,
-	0x2f, 0xf8, 0x1b, 0x6b, 0xf8, 0xf5, 0x74, 0x3a, 0x27, 0x0f, 0xa1, 0x49, 0x92, 0x44, 0x8a, 0x8b,
-	0x15, 0x81, 0xca, 0xba, 0x4d, 0xe4, 0x8c, 0x5c, 0xe4, 0x21, 0x40, 0x1c, 0x15, 0xf4, 0xcd, 0x35,
-	0xf4, 0x5a, 0x1c, 0xe5, 0x44, 0x02, 0xdb, 0x5a, 0x68, 0x72, 0x1e, 0x52, 0x4c, 0x84, 0x62, 0xda,
-	0xab, 0x66, 0xdc, 0x2f, 0x5e, 0xbf, 0xbb, 0x57, 0xfa, 0xfb, 0xdd, 0xbd, 0xc3, 0x98, 0xe9, 0xe7,
-	0x8b, 0x59, 0x2f, 0x12, 0x73, 0x9b, 0x24, 0xfb, 0xd3, 0x55, 0xf4, 0x85, 0x0d, 0xe9, 0x84, 0xeb,
-	0xb7, 0xaf, 0xba, 0x60, 0x57, 0x9a, 0x70, 0xed, 0xdf, 0xcd, 0x24, 0x47, 0x46, 0xd1, 0xed, 0x42,
-	0x55, 0x69, 0xa2, 0x17, 0xca, 0xdb, 0x3a, 0x70, 0x8e, 0x76, 0x8e, 0xf7, 0x7a, 0xff, 0xc9, 0x73,
-	0x2f, 0xc8, 0x9a, 0xbe, 0x1d, 0x4a, 0x33, 0x86, 0x9c, 0x26, 0x82, 0x71, 0xed, 0xdd, 0x31, 0x19,
-	0xcb, 0x6b, 0xf7, 0x04, 0xea, 0xf4, 0x3a, 0xa8, 0x5e, 0xed, 0xc0, 0x39, 0xaa, 0x1f, 0xb7, 0x6e,
-	0xe8, 0xad, 0x44, 0xf9, 0xa4, 0x92, 0xee, 0xc3, 0x5f, 0x25, 0x75, 0x96, 0x00, 0x3e, 0xbe, 0x24,
-	0x92, 0x4e, 0xf8, 0x99, 0x70, 0x8f, 0x61, 0xeb, 0x7d, 0x93, 0x93, 0x0f, 0xba, 0x0f, 0xa1, 0x4a,
-	0xe6, 0x62, 0xc1, 0x75, 0x16, 0x94, 0xfa, 0xf1, 0x7e, 0xcf, 0xce, 0xa7, 0x5f, 0x64, 0xcf, 0x7e,
-	0x91, 0xbd, 0xa1, 0x60, 0xf9, 0xfa, 0x76, 0xbc, 0xf3, 0x67, 0x19, 0x76, 0x82, 0xa4, 0x48, 0x31,
-	0x8b, 0x30, 0xbd, 0x38, 0x95, 0xbc, 0x77, 0x78, 0x6b, 0x2a, 0xc9, 0x2f, 0xee, 0x10, 0x1a, 0x8b,
-	0x84, 0x12, 0x8d, 0xa1, 0x66, 0x73, 0x0c, 0x15, 0x46, 0x99, 0x9b, 0x0d, 0x7f, 0xdb, 0xc0, 0x53,
-	0x36, 0xc7, 0x00, 0x23, 0xf7, 0x7b, 0x00, 0x89, 0x84, 0x86, 0x49, 0xba, 0x9c, 0x4d, 0xe6, 0x87,
-	0xdc, 0xee, 0x08, 0xa3, 0x95, 0xdb, 0x1d, 0x61, 0xe4, 0xd7, 0x52, 0x3d, 0xe3, 0xfe, 0x10, 0x1a,
-	0x67, 0x12, 0x31, 0xcc, 0x56, 0xf8, 0x71, 0x21, 0x34, 0xc9, 0xa2, 0x5b, 0xf1, 0xb7, 0x53, 0xd8,
-	0x47, 0x42, 0xbf, 0x4e, 0x41, 0xf7, 0x07, 0xa8, 0x2b, 0x2d, 0x24, 0x5a, 0x17, 0x9b, 0xb7, 0xe0,
-	0x02, 0x32, 0xc1, 0xcc, 0x46, 0xe7, 0x57, 0x07, 0xf6, 0x02, 0x8c, 0x04, 0xa7, 0x44, 0x2e, 0xcd,
-	0x01, 0x63, 0x61, 0xf0, 0xe6, 0x29, 0x39, 0xff, 0x77, 0x4a, 0x37, 0x0c, 0x96, 0x6f, 0xd7, 0xe0,
-	0x7d, 0x05, 0x55, 0x93, 0x72, 0x77, 0x0f, 0x3e, 0x0a, 0xa6, 0x83, 0xe9, 0x69, 0x10, 0x4e, 0x9e,
-	0x86, 0xc1, 0xd8, 0xff, 0x66, 0x32, 0x1c, 0x37, 0x4b, 0xee, 0x2e, 0x34, 0xaf, 0xe1, 0x2f, 0x07,
-	0x93, 0x27, 0xe3, 0x51, 0xd3, 0x71, 0x3f, 0x81, 0x8f, 0x2d, 0xfa, 0xd8, 0x1f, 0x0c, 0xc7, 0x8f,
-	0x4e, 0x9f, 0x84, 0xe3, 0x6f, 0x27, 0xd3, 0xc9, 0xd3, 0xc7, 0xcd, 0xb2, 0xbb, 0x0f, 0x7b, 0xb6,
-	0xf9, 0xec, 0x74, 0x1a, 0x3e, 0x7b, 0x54, 0xa8, 0x6d, 0xb4, 0x2a, 0x3f, 0xff, 0xde, 0x2e, 0x9d,
-	0x8c, 0x5e, 0x5f, 0xb6, 0x9d, 0x37, 0x97, 0x6d, 0xe7, 0x9f, 0xcb, 0xb6, 0xf3, 0xcb, 0x55, 0xbb,
-	0xf4, 0xe6, 0xaa, 0x5d, 0xfa, 0xeb, 0xaa, 0x5d, 0xfa, 0xee, 0xfe, 0xca, 0x86, 0x66, 0x7c, 0xd6,
-	0x8d, 0x9e, 0x13, 0xc6, 0xfb, 0x2b, 0x0f, 0xd0, 0x4f, 0xc5, 0x13, 0x34, 0xab, 0x66, 0x6f, 0xc4,
-	0x67, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0xb2, 0xb5, 0x6b, 0xc4, 0xa0, 0x06, 0x00, 0x00,
+	// 835 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0xd1, 0x6e, 0x1b, 0x45,
+	0x14, 0xf5, 0x3a, 0x8e, 0xd3, 0x5c, 0xc7, 0xb1, 0x19, 0x12, 0x75, 0x63, 0x24, 0x37, 0xf2, 0x43,
+	0x14, 0x2a, 0xd9, 0x56, 0xc3, 0x43, 0x1f, 0xe0, 0xc5, 0xb1, 0xdd, 0xca, 0x50, 0xb5, 0xb0, 0x76,
+	0x10, 0x02, 0xa1, 0xd5, 0x78, 0xe7, 0x66, 0x3b, 0x8a, 0x3d, 0xb3, 0xcc, 0x8c, 0x53, 0xfc, 0x07,
+	0x3c, 0xf2, 0x05, 0x3c, 0xc0, 0x17, 0x20, 0xf5, 0x23, 0xfa, 0x58, 0xf5, 0x09, 0xf1, 0x50, 0xa1,
+	0xe4, 0x03, 0xf8, 0x05, 0xb4, 0xbb, 0xb3, 0x1b, 0x13, 0x21, 0x59, 0x48, 0x79, 0xb2, 0xef, 0xb9,
+	0xf7, 0x9c, 0x3d, 0x73, 0xf7, 0xac, 0x06, 0x0e, 0x42, 0x85, 0x28, 0xce, 0x39, 0xce, 0x58, 0x57,
+	0x47, 0x5d, 0xb3, 0x8c, 0x50, 0x77, 0x22, 0x25, 0x8d, 0x24, 0xd5, 0x9b, 0x56, 0x47, 0x47, 0x8d,
+	0x66, 0x20, 0xf5, 0x5c, 0xea, 0xee, 0x94, 0x6a, 0xec, 0x5e, 0x3e, 0x9a, 0xa2, 0xa1, 0x8f, 0xba,
+	0x81, 0xe4, 0x22, 0x1d, 0x6f, 0x1c, 0xa4, 0x7d, 0x3f, 0xa9, 0xba, 0x69, 0x61, 0x5b, 0x7b, 0xa1,
+	0x0c, 0x65, 0x8a, 0xc7, 0xff, 0x52, 0xb4, 0xf5, 0xab, 0x03, 0x95, 0x01, 0xea, 0x40, 0xf1, 0xc8,
+	0x70, 0x29, 0x88, 0x0b, 0x5b, 0x73, 0x29, 0xf8, 0x05, 0x2a, 0xd7, 0x39, 0x74, 0x8e, 0xb7, 0xbd,
+	0xac, 0x24, 0x0d, 0xb8, 0xc7, 0x19, 0x0a, 0xc3, 0xcd, 0xd2, 0x2d, 0x26, 0xad, 0xbc, 0x8e, 0x59,
+	0xaf, 0x70, 0xaa, 0xb9, 0x41, 0x77, 0x23, 0x65, 0xd9, 0x92, 0x7c, 0x0c, 0x75, 0x8d, 0xc1, 0x42,
+	0x71, 0xb3, 0xf4, 0x03, 0x29, 0x0c, 0x0d, 0x8c, 0x5b, 0x4a, 0x46, 0x6a, 0x19, 0xde, 0x4f, 0xe1,
+	0x58, 0x84, 0xa1, 0xa1, 0x7c, 0xa6, 0xdd, 0xcd, 0x54, 0xc4, 0x96, 0xad, 0xbf, 0x4b, 0x50, 0x1b,
+	0x1b, 0xa9, 0x68, 0x88, 0x5f, 0x2a, 0x79, 0xc9, 0x19, 0x2a, 0xb2, 0x0b, 0x45, 0xce, 0x12, 0x8f,
+	0x55, 0xaf, 0xc8, 0x19, 0xe9, 0x43, 0x5d, 0x46, 0xa8, 0xa8, 0x91, 0xca, 0xa7, 0x8c, 0x29, 0xd4,
+	0x3a, 0xb5, 0x79, 0xea, 0xbe, 0x7b, 0xdd, 0xde, 0xb3, 0xab, 0xe8, 0xa5, 0x9d, 0xb1, 0x51, 0x5c,
+	0x84, 0x5e, 0x2d, 0x63, 0x58, 0x98, 0xf4, 0xa0, 0x76, 0xbe, 0x10, 0x8c, 0x8b, 0x30, 0xd7, 0xd8,
+	0x58, 0xa3, 0xb1, 0x6b, 0x09, 0x99, 0xc4, 0xa7, 0xb0, 0xa3, 0x91, 0xce, 0x72, 0x7e, 0x69, 0x0d,
+	0xbf, 0x12, 0x4f, 0x67, 0xe4, 0x3e, 0xd4, 0x69, 0x14, 0x29, 0x79, 0xb9, 0x22, 0xb0, 0xb9, 0xee,
+	0x10, 0x19, 0x23, 0x13, 0x79, 0x0c, 0x10, 0x06, 0x39, 0xbd, 0xbc, 0x86, 0xbe, 0x1d, 0x06, 0x19,
+	0x91, 0x42, 0xd5, 0x48, 0x43, 0x67, 0x3e, 0xc3, 0x48, 0x6a, 0x6e, 0xdc, 0xad, 0x84, 0xfb, 0xd9,
+	0x9b, 0xf7, 0x0f, 0x0a, 0x7f, 0xbe, 0x7f, 0x70, 0x14, 0x72, 0xf3, 0x72, 0x31, 0xed, 0x04, 0x72,
+	0x6e, 0x93, 0x65, 0x7f, 0xda, 0x9a, 0x5d, 0xd8, 0xd0, 0x8e, 0x84, 0x79, 0xf7, 0xba, 0x0d, 0xf6,
+	0x49, 0x23, 0x61, 0xbc, 0x9d, 0x44, 0x72, 0x90, 0x2a, 0x92, 0x36, 0x94, 0xb5, 0xa1, 0x66, 0xa1,
+	0xdd, 0x7b, 0x87, 0xce, 0xf1, 0xee, 0xc9, 0x7e, 0xe7, 0x5f, 0xf9, 0xee, 0x8c, 0x93, 0xa6, 0x67,
+	0x87, 0xe2, 0xcc, 0xa1, 0x60, 0x91, 0xe4, 0xc2, 0xb8, 0xdb, 0x69, 0xe6, 0xb2, 0x9a, 0x9c, 0x42,
+	0x85, 0xdd, 0x04, 0xd7, 0x85, 0x43, 0xe7, 0xb8, 0x72, 0xd2, 0xb8, 0xa5, 0xb7, 0x12, 0xed, 0xd3,
+	0x52, 0x7c, 0x0e, 0x6f, 0x95, 0x44, 0xee, 0xc3, 0xd6, 0x74, 0xa6, 0xfd, 0x0b, 0x5c, 0xba, 0x95,
+	0x43, 0xe7, 0x78, 0xc7, 0x2b, 0x4f, 0x67, 0xfa, 0x0b, 0x5c, 0xb6, 0x96, 0x00, 0x1e, 0xbe, 0xa2,
+	0x8a, 0x8d, 0xc4, 0xb9, 0x24, 0x27, 0xb0, 0x95, 0xad, 0xd3, 0x59, 0xb3, 0xce, 0x6c, 0x90, 0x3c,
+	0x86, 0x32, 0x9d, 0xcb, 0x85, 0x30, 0x49, 0x0a, 0x2b, 0x27, 0x07, 0x1d, 0x3b, 0x1f, 0x7f, 0xba,
+	0x1d, 0xfb, 0xe9, 0x76, 0xfa, 0x92, 0x67, 0xc6, 0xec, 0x78, 0xeb, 0xf7, 0x22, 0xec, 0x8e, 0xa3,
+	0x3c, 0xee, 0x3c, 0x40, 0xf2, 0x21, 0x6c, 0xea, 0xc8, 0xcf, 0xe3, 0x5e, 0xd2, 0xd1, 0x88, 0x91,
+	0x23, 0xa8, 0x2d, 0x22, 0x46, 0x0d, 0xfa, 0x86, 0xcf, 0xd1, 0xd7, 0x18, 0x24, 0x4f, 0xda, 0xf0,
+	0xaa, 0x29, 0x3c, 0xe1, 0x73, 0x1c, 0x63, 0x40, 0xbe, 0x03, 0x50, 0x48, 0x99, 0x1f, 0xc5, 0x52,
+	0x36, 0xce, 0xff, 0xe7, 0x95, 0x0e, 0x30, 0x58, 0x79, 0xa5, 0x03, 0x0c, 0xbc, 0xed, 0x58, 0x2f,
+	0x75, 0x76, 0x04, 0xb5, 0x73, 0x85, 0xe8, 0x27, 0x4f, 0xf8, 0x61, 0x21, 0x0d, 0x4d, 0x02, 0x5f,
+	0xf2, 0xaa, 0x31, 0xec, 0x21, 0x65, 0x5f, 0xc5, 0x20, 0xf9, 0x1e, 0x2a, 0xda, 0x48, 0x85, 0xd6,
+	0xc5, 0xe6, 0x1d, 0xb8, 0x80, 0x44, 0x30, 0xb1, 0xd1, 0xfa, 0xc5, 0x81, 0xfd, 0x31, 0x06, 0x52,
+	0x30, 0xaa, 0x96, 0xe9, 0xf2, 0x30, 0x37, 0x78, 0x7b, 0x4b, 0xce, 0x7f, 0x6d, 0xe9, 0x96, 0xc1,
+	0xe2, 0xdd, 0x1a, 0x7c, 0xa8, 0xa1, 0x9c, 0x46, 0x9b, 0xec, 0xc3, 0x07, 0xe3, 0x49, 0x6f, 0x72,
+	0x36, 0xf6, 0x47, 0xcf, 0xfd, 0xf1, 0xd0, 0xfb, 0x7a, 0xd4, 0x1f, 0xd6, 0x0b, 0x64, 0x0f, 0xea,
+	0x37, 0xf0, 0xe7, 0xbd, 0xd1, 0xb3, 0xe1, 0xa0, 0xee, 0x90, 0x8f, 0xe0, 0xbe, 0x45, 0x9f, 0x7a,
+	0xbd, 0xfe, 0xf0, 0xc9, 0xd9, 0x33, 0x7f, 0xf8, 0xcd, 0x68, 0x32, 0x7a, 0xfe, 0xb4, 0x5e, 0x24,
+	0x07, 0xb0, 0x6f, 0x9b, 0x2f, 0xce, 0x26, 0xfe, 0x8b, 0x27, 0xb9, 0xda, 0x46, 0xa3, 0xf4, 0xd3,
+	0x6f, 0xcd, 0xc2, 0xe9, 0xe0, 0xcd, 0x55, 0xd3, 0x79, 0x7b, 0xd5, 0x74, 0xfe, 0xba, 0x6a, 0x3a,
+	0x3f, 0x5f, 0x37, 0x0b, 0x6f, 0xaf, 0x9b, 0x85, 0x3f, 0xae, 0x9b, 0x85, 0x6f, 0x1f, 0xae, 0x1c,
+	0x68, 0x2a, 0xa6, 0xed, 0xe0, 0x25, 0xe5, 0xa2, 0xbb, 0x72, 0x0b, 0xfd, 0x98, 0xdf, 0x43, 0xd3,
+	0x72, 0x72, 0x51, 0x7c, 0xf2, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x9a, 0xe6, 0xec, 0x1d, 0xa5,
+	0x06, 0x00, 0x00,
 }
 
 func (m *Description) Marshal() (dAtA []byte, err error) {
@@ -562,6 +583,13 @@ func (m *StorageProvider) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	_ = i
 	var l int
 	_ = l
+	if len(m.BlsKey) > 0 {
+		i -= len(m.BlsKey)
+		copy(dAtA[i:], m.BlsKey)
+		i = encodeVarintTypes(dAtA, i, uint64(len(m.BlsKey)))
+		i--
+		dAtA[i] = 0x5a
+	}
 	{
 		size, err := m.Description.MarshalToSizedBuffer(dAtA[:i])
 		if err != nil {
@@ -571,18 +599,18 @@ func (m *StorageProvider) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		i = encodeVarintTypes(dAtA, i, uint64(size))
 	}
 	i--
-	dAtA[i] = 0x4a
+	dAtA[i] = 0x52
 	if len(m.Endpoint) > 0 {
 		i -= len(m.Endpoint)
 		copy(dAtA[i:], m.Endpoint)
 		i = encodeVarintTypes(dAtA, i, uint64(len(m.Endpoint)))
 		i--
-		dAtA[i] = 0x42
+		dAtA[i] = 0x4a
 	}
 	if m.Status != 0 {
 		i = encodeVarintTypes(dAtA, i, uint64(m.Status))
 		i--
-		dAtA[i] = 0x38
+		dAtA[i] = 0x40
 	}
 	{
 		size := m.TotalDeposit.Size()
@@ -593,41 +621,46 @@ func (m *StorageProvider) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		i = encodeVarintTypes(dAtA, i, uint64(size))
 	}
 	i--
-	dAtA[i] = 0x32
+	dAtA[i] = 0x3a
 	if len(m.GcAddress) > 0 {
 		i -= len(m.GcAddress)
 		copy(dAtA[i:], m.GcAddress)
 		i = encodeVarintTypes(dAtA, i, uint64(len(m.GcAddress)))
 		i--
-		dAtA[i] = 0x2a
+		dAtA[i] = 0x32
 	}
 	if len(m.ApprovalAddress) > 0 {
 		i -= len(m.ApprovalAddress)
 		copy(dAtA[i:], m.ApprovalAddress)
 		i = encodeVarintTypes(dAtA, i, uint64(len(m.ApprovalAddress)))
 		i--
-		dAtA[i] = 0x22
+		dAtA[i] = 0x2a
 	}
 	if len(m.SealAddress) > 0 {
 		i -= len(m.SealAddress)
 		copy(dAtA[i:], m.SealAddress)
 		i = encodeVarintTypes(dAtA, i, uint64(len(m.SealAddress)))
 		i--
-		dAtA[i] = 0x1a
+		dAtA[i] = 0x22
 	}
 	if len(m.FundingAddress) > 0 {
 		i -= len(m.FundingAddress)
 		copy(dAtA[i:], m.FundingAddress)
 		i = encodeVarintTypes(dAtA, i, uint64(len(m.FundingAddress)))
 		i--
-		dAtA[i] = 0x12
+		dAtA[i] = 0x1a
 	}
 	if len(m.OperatorAddress) > 0 {
 		i -= len(m.OperatorAddress)
 		copy(dAtA[i:], m.OperatorAddress)
 		i = encodeVarintTypes(dAtA, i, uint64(len(m.OperatorAddress)))
 		i--
-		dAtA[i] = 0xa
+		dAtA[i] = 0x12
+	}
+	if m.Id != 0 {
+		i = encodeVarintTypes(dAtA, i, uint64(m.Id))
+		i--
+		dAtA[i] = 0x8
 	}
 	return len(dAtA) - i, nil
 }
@@ -722,12 +755,10 @@ func (m *SpStoragePrice) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		i--
 		dAtA[i] = 0x10
 	}
-	if len(m.SpAddress) > 0 {
-		i -= len(m.SpAddress)
-		copy(dAtA[i:], m.SpAddress)
-		i = encodeVarintTypes(dAtA, i, uint64(len(m.SpAddress)))
+	if m.SpId != 0 {
+		i = encodeVarintTypes(dAtA, i, uint64(m.SpId))
 		i--
-		dAtA[i] = 0xa
+		dAtA[i] = 0x8
 	}
 	return len(dAtA) - i, nil
 }
@@ -816,6 +847,9 @@ func (m *StorageProvider) Size() (n int) {
 	}
 	var l int
 	_ = l
+	if m.Id != 0 {
+		n += 1 + sovTypes(uint64(m.Id))
+	}
 	l = len(m.OperatorAddress)
 	if l > 0 {
 		n += 1 + l + sovTypes(uint64(l))
@@ -847,6 +881,10 @@ func (m *StorageProvider) Size() (n int) {
 	}
 	l = m.Description.Size()
 	n += 1 + l + sovTypes(uint64(l))
+	l = len(m.BlsKey)
+	if l > 0 {
+		n += 1 + l + sovTypes(uint64(l))
+	}
 	return n
 }
 
@@ -871,9 +909,8 @@ func (m *SpStoragePrice) Size() (n int) {
 	}
 	var l int
 	_ = l
-	l = len(m.SpAddress)
-	if l > 0 {
-		n += 1 + l + sovTypes(uint64(l))
+	if m.SpId != 0 {
+		n += 1 + sovTypes(uint64(m.SpId))
 	}
 	if m.UpdateTimeSec != 0 {
 		n += 1 + sovTypes(uint64(m.UpdateTimeSec))
@@ -1148,6 +1185,25 @@ func (m *StorageProvider) Unmarshal(dAtA []byte) error {
 		}
 		switch fieldNum {
 		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType)
+			}
+			m.Id = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTypes
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.Id |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field OperatorAddress", wireType)
 			}
@@ -1179,7 +1235,7 @@ func (m *StorageProvider) Unmarshal(dAtA []byte) error {
 			}
 			m.OperatorAddress = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
-		case 2:
+		case 3:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field FundingAddress", wireType)
 			}
@@ -1211,7 +1267,7 @@ func (m *StorageProvider) Unmarshal(dAtA []byte) error {
 			}
 			m.FundingAddress = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
-		case 3:
+		case 4:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field SealAddress", wireType)
 			}
@@ -1243,7 +1299,7 @@ func (m *StorageProvider) Unmarshal(dAtA []byte) error {
 			}
 			m.SealAddress = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
-		case 4:
+		case 5:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field ApprovalAddress", wireType)
 			}
@@ -1275,7 +1331,7 @@ func (m *StorageProvider) Unmarshal(dAtA []byte) error {
 			}
 			m.ApprovalAddress = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
-		case 5:
+		case 6:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field GcAddress", wireType)
 			}
@@ -1307,7 +1363,7 @@ func (m *StorageProvider) Unmarshal(dAtA []byte) error {
 			}
 			m.GcAddress = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
-		case 6:
+		case 7:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field TotalDeposit", wireType)
 			}
@@ -1341,7 +1397,7 @@ func (m *StorageProvider) Unmarshal(dAtA []byte) error {
 				return err
 			}
 			iNdEx = postIndex
-		case 7:
+		case 8:
 			if wireType != 0 {
 				return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType)
 			}
@@ -1360,7 +1416,7 @@ func (m *StorageProvider) Unmarshal(dAtA []byte) error {
 					break
 				}
 			}
-		case 8:
+		case 9:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field Endpoint", wireType)
 			}
@@ -1392,7 +1448,7 @@ func (m *StorageProvider) Unmarshal(dAtA []byte) error {
 			}
 			m.Endpoint = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
-		case 9:
+		case 10:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType)
 			}
@@ -1425,6 +1481,40 @@ func (m *StorageProvider) Unmarshal(dAtA []byte) error {
 				return err
 			}
 			iNdEx = postIndex
+		case 11:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field BlsKey", wireType)
+			}
+			var byteLen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTypes
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				byteLen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if byteLen < 0 {
+				return ErrInvalidLengthTypes
+			}
+			postIndex := iNdEx + byteLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTypes
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.BlsKey = append(m.BlsKey[:0], dAtA[iNdEx:postIndex]...)
+			if m.BlsKey == nil {
+				m.BlsKey = []byte{}
+			}
+			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipTypes(dAtA[iNdEx:])
@@ -1591,10 +1681,10 @@ func (m *SpStoragePrice) Unmarshal(dAtA []byte) error {
 		}
 		switch fieldNum {
 		case 1:
-			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field SpAddress", wireType)
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field SpId", wireType)
 			}
-			var stringLen uint64
+			m.SpId = 0
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowTypes
@@ -1604,24 +1694,11 @@ func (m *SpStoragePrice) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				stringLen |= uint64(b&0x7F) << shift
+				m.SpId |= uint32(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
-			intStringLen := int(stringLen)
-			if intStringLen < 0 {
-				return ErrInvalidLengthTypes
-			}
-			postIndex := iNdEx + intStringLen
-			if postIndex < 0 {
-				return ErrInvalidLengthTypes
-			}
-			if postIndex > l {
-				return io.ErrUnexpectedEOF
-			}
-			m.SpAddress = string(dAtA[iNdEx:postIndex])
-			iNdEx = postIndex
 		case 2:
 			if wireType != 0 {
 				return fmt.Errorf("proto: wrong wireType = %d for field UpdateTimeSec", wireType)
diff --git a/x/storage/abci.go b/x/storage/abci.go
index 321a25fcb..e42e1b51b 100644
--- a/x/storage/abci.go
+++ b/x/storage/abci.go
@@ -23,12 +23,14 @@ func EndBlocker(ctx sdk.Context, keeper k.Keeper) {
 	}
 
 	blockTime := ctx.BlockTime().Unix()
-	// set ForceUpdateFrozenStreamRecordKey to true in context to force update frozen stream record
-	ctx = ctx.WithValue(paymenttypes.ForceUpdateFrozenStreamRecordKey, true)
+
+	// set ForceUpdateStreamRecordKey to true in context to force update frozen stream record
+	ctx = ctx.WithValue(paymenttypes.ForceUpdateStreamRecordKey, true)
+
 	// delete objects
 	deleted, err := keeper.DeleteDiscontinueObjectsUntil(ctx, blockTime, deletionMax)
 	if err != nil {
-		panic("fail to delete objects, err " + err.Error())
+		ctx.Logger().Error("should not happen, fail to delete objects, err " + err.Error())
 	}
 
 	if deleted >= deletionMax {
@@ -38,7 +40,7 @@ func EndBlocker(ctx sdk.Context, keeper k.Keeper) {
 	// delete buckets
 	_, err = keeper.DeleteDiscontinueBucketsUntil(ctx, blockTime, deletionMax-deleted)
 	if err != nil {
-		panic("fail to delete buckets, err " + err.Error())
+		ctx.Logger().Error("should not happen, fail to delete buckets, err " + err.Error())
 	}
 	keeper.PersistDeleteInfo(ctx)
 
diff --git a/x/storage/client/cli/tx.go b/x/storage/client/cli/tx.go
index 20dfa9e28..62c193b2c 100644
--- a/x/storage/client/cli/tx.go
+++ b/x/storage/client/cli/tx.go
@@ -21,7 +21,7 @@ import (
 
 // GetTxCmd returns the transaction commands for this module
 func GetTxCmd() *cobra.Command {
-	storageTxCmd := &cobra.Command{
+	cmd := &cobra.Command{
 		Use:                        types.ModuleName,
 		Short:                      fmt.Sprintf("%s transactions subcommands", types.ModuleName),
 		DisableFlagParsing:         true,
@@ -29,7 +29,7 @@ func GetTxCmd() *cobra.Command {
 		RunE:                       client.ValidateCmd,
 	}
 
-	storageTxCmd.AddCommand(
+	cmd.AddCommand(
 		CmdCreateBucket(),
 		CmdDeleteBucket(),
 		CmdUpdateBucketInfo(),
@@ -37,7 +37,7 @@ func GetTxCmd() *cobra.Command {
 		CmdDiscontinueBucket(),
 	)
 
-	storageTxCmd.AddCommand(
+	cmd.AddCommand(
 		CmdCreateObject(),
 		CmdDeleteObject(),
 		CmdCancelCreateObject(),
@@ -47,7 +47,7 @@ func GetTxCmd() *cobra.Command {
 		CmdUpdateObjectInfo(),
 	)
 
-	storageTxCmd.AddCommand(
+	cmd.AddCommand(
 		CmdCreateGroup(),
 		CmdDeleteGroup(),
 		CmdUpdateGroupMember(),
@@ -56,14 +56,15 @@ func GetTxCmd() *cobra.Command {
 		CmdMirrorGroup(),
 	)
 
-	storageTxCmd.AddCommand(
+	cmd.AddCommand(
 		CmdPutPolicy(),
 		CmdDeletePolicy(),
 	)
 
+	cmd.AddCommand(CmdCancelMigrateBucket())
 	// this line is used by starport scaffolding # 1
 
-	return storageTxCmd
+	return cmd
 }
 
 // CmdCreateBucket returns a CLI command handler for creating a MsgCreateBucket transaction.
@@ -318,7 +319,6 @@ func CmdCreateObject() *cobra.Command {
 				redundancyType,
 				approveTimeoutHeight,
 				approveSignatureBytes,
-				nil,
 			)
 			primarySP, err := cmd.Flags().GetString(FlagPrimarySP)
 			if err != nil {
diff --git a/x/storage/client/cli/tx_cancel_migrate_bucket.go b/x/storage/client/cli/tx_cancel_migrate_bucket.go
new file mode 100644
index 000000000..b3a32a621
--- /dev/null
+++ b/x/storage/client/cli/tx_cancel_migrate_bucket.go
@@ -0,0 +1,42 @@
+package cli
+
+import (
+	"strconv"
+
+	"github.com/cosmos/cosmos-sdk/client"
+	"github.com/cosmos/cosmos-sdk/client/flags"
+	"github.com/cosmos/cosmos-sdk/client/tx"
+	"github.com/spf13/cobra"
+
+	"github.com/bnb-chain/greenfield/x/storage/types"
+)
+
+var _ = strconv.Itoa(0)
+
+func CmdCancelMigrateBucket() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "cancel-migrate-bucket",
+		Short: "Broadcast message CancelMigrateBucket",
+		Args:  cobra.ExactArgs(0),
+		RunE: func(cmd *cobra.Command, args []string) (err error) {
+
+			clientCtx, err := client.GetClientTxContext(cmd)
+			if err != nil {
+				return err
+			}
+
+			msg := types.NewMsgCancelMigrateBucket(
+				clientCtx.GetFromAddress(),
+				"bucket-name",
+			)
+			if err := msg.ValidateBasic(); err != nil {
+				return err
+			}
+			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
+		},
+	}
+
+	flags.AddTxFlagsToCmd(cmd)
+
+	return cmd
+}
diff --git a/x/storage/genesis_test.go b/x/storage/genesis_test.go
index 849a9a1a5..56471b5f3 100644
--- a/x/storage/genesis_test.go
+++ b/x/storage/genesis_test.go
@@ -24,7 +24,6 @@ func makeKeeper(t *testing.T) (*keeper.Keeper, sdk.Context) {
 	tStoreKey := storetypes.NewTransientStoreKey(types.TStoreKey)
 
 	testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
-
 	k := keeper.NewKeeper(
 		encCfg.Codec,
 		key,
@@ -34,6 +33,7 @@ func makeKeeper(t *testing.T) (*keeper.Keeper, sdk.Context) {
 		&types.MockPaymentKeeper{},
 		&types.MockPermissionKeeper{},
 		&types.MockCrossChainKeeper{},
+		&types.MockVirtualGroupKeeper{},
 		authtypes.NewModuleAddress(govtypes.ModuleName).String(),
 	)
 
diff --git a/x/storage/keeper/cross_app_bucket.go b/x/storage/keeper/cross_app_bucket.go
index a219eddec..a3d85a08d 100644
--- a/x/storage/keeper/cross_app_bucket.go
+++ b/x/storage/keeper/cross_app_bucket.go
@@ -4,9 +4,9 @@ import (
 	"encoding/hex"
 
 	"cosmossdk.io/math"
-	"github.com/cosmos/cosmos-sdk/bsc/rlp"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 
+	"github.com/bnb-chain/greenfield/types/common"
 	"github.com/bnb-chain/greenfield/x/storage/types"
 )
 
@@ -50,11 +50,7 @@ func (app *BucketApp) ExecuteAckPackage(ctx sdk.Context, appCtx *sdk.CrossChainA
 			OperationType: operationType,
 			Package:       result.Payload,
 		}
-		wrapPayloadBts, err := rlp.EncodeToBytes(wrapPayload)
-		if err != nil {
-			panic(err)
-		}
-		result.Payload = wrapPayloadBts
+		result.Payload = wrapPayload.MustSerialize()
 	}
 
 	return result
@@ -88,11 +84,7 @@ func (app *BucketApp) ExecuteFailAckPackage(ctx sdk.Context, appCtx *sdk.CrossCh
 			OperationType: operationType,
 			Package:       result.Payload,
 		}
-		wrapPayloadBts, err := rlp.EncodeToBytes(wrapPayload)
-		if err != nil {
-			panic(err)
-		}
-		result.Payload = wrapPayloadBts
+		result.Payload = wrapPayload.MustSerialize()
 	}
 
 	return result
@@ -126,11 +118,7 @@ func (app *BucketApp) ExecuteSynPackage(ctx sdk.Context, appCtx *sdk.CrossChainA
 			OperationType: operationType,
 			Package:       result.Payload,
 		}
-		wrapPayloadBts, err := rlp.EncodeToBytes(wrapPayload)
-		if err != nil {
-			panic(err)
-		}
-		result.Payload = wrapPayloadBts
+		result.Payload = wrapPayload.MustSerialize()
 	}
 
 	return result
@@ -153,9 +141,10 @@ func (app *BucketApp) handleMirrorBucketAckPackage(ctx sdk.Context, appCtx *sdk.
 	}
 
 	if err := ctx.EventManager().EmitTypedEvents(&types.EventMirrorBucketResult{
-		Status:     uint32(ackPackage.Status),
-		BucketName: bucketInfo.BucketName,
-		BucketId:   bucketInfo.Id,
+		Status:      uint32(ackPackage.Status),
+		BucketName:  bucketInfo.BucketName,
+		BucketId:    bucketInfo.Id,
+		DestChainId: uint32(appCtx.SrcChainId),
 	}); err != nil {
 		return sdk.ExecuteResult{
 			Err: err,
@@ -178,9 +167,10 @@ func (app *BucketApp) handleMirrorBucketFailAckPackage(ctx sdk.Context, appCtx *
 	app.storageKeeper.SetBucketInfo(ctx, bucketInfo)
 
 	if err := ctx.EventManager().EmitTypedEvents(&types.EventMirrorBucketResult{
-		Status:     uint32(types.StatusFail),
-		BucketName: bucketInfo.BucketName,
-		BucketId:   bucketInfo.Id,
+		Status:      uint32(types.StatusFail),
+		BucketName:  bucketInfo.BucketName,
+		BucketId:    bucketInfo.Id,
+		DestChainId: uint32(appCtx.SrcChainId),
 	}); err != nil {
 		return sdk.ExecuteResult{
 			Err: err,
@@ -231,7 +221,7 @@ func (app *BucketApp) handleCreateBucketSynPackage(ctx sdk.Context, appCtx *sdk.
 			SourceType:       types.SOURCE_TYPE_BSC_CROSS_CHAIN,
 			ChargedReadQuota: createBucketPackage.ChargedReadQuota,
 			PaymentAddress:   createBucketPackage.PaymentAddress.String(),
-			PrimarySpApproval: &types.Approval{
+			PrimarySpApproval: &common.Approval{
 				ExpiredHeight: createBucketPackage.PrimarySpApprovalExpiredHeight,
 				Sig:           createBucketPackage.PrimarySpApprovalSignature,
 			},
diff --git a/x/storage/keeper/cross_app_group.go b/x/storage/keeper/cross_app_group.go
index e6c1754f0..0f09fcceb 100644
--- a/x/storage/keeper/cross_app_group.go
+++ b/x/storage/keeper/cross_app_group.go
@@ -4,7 +4,6 @@ import (
 	"encoding/hex"
 
 	"cosmossdk.io/math"
-	"github.com/cosmos/cosmos-sdk/bsc/rlp"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 
 	"github.com/bnb-chain/greenfield/x/storage/types"
@@ -53,11 +52,7 @@ func (app *GroupApp) ExecuteAckPackage(ctx sdk.Context, appCtx *sdk.CrossChainAp
 			OperationType: operationType,
 			Package:       result.Payload,
 		}
-		wrapPayloadBts, err := rlp.EncodeToBytes(wrapPayload)
-		if err != nil {
-			panic(err)
-		}
-		result.Payload = wrapPayloadBts
+		result.Payload = wrapPayload.MustSerialize()
 	}
 
 	return result
@@ -94,11 +89,7 @@ func (app *GroupApp) ExecuteFailAckPackage(ctx sdk.Context, appCtx *sdk.CrossCha
 			OperationType: operationType,
 			Package:       result.Payload,
 		}
-		wrapPayloadBts, err := rlp.EncodeToBytes(wrapPayload)
-		if err != nil {
-			panic(err)
-		}
-		result.Payload = wrapPayloadBts
+		result.Payload = wrapPayload.MustSerialize()
 	}
 
 	return result
@@ -137,11 +128,7 @@ func (app *GroupApp) ExecuteSynPackage(ctx sdk.Context, appCtx *sdk.CrossChainAp
 			OperationType: operationType,
 			Package:       result.Payload,
 		}
-		wrapPayloadBts, err := rlp.EncodeToBytes(wrapPayload)
-		if err != nil {
-			panic(err)
-		}
-		result.Payload = wrapPayloadBts
+		result.Payload = wrapPayload.MustSerialize()
 	}
 
 	return result
@@ -280,9 +267,10 @@ func (app *GroupApp) handleMirrorGroupAckPackage(ctx sdk.Context, appCtx *sdk.Cr
 	}
 
 	if err := ctx.EventManager().EmitTypedEvents(&types.EventMirrorGroupResult{
-		Status:    uint32(ackPackage.Status),
-		GroupName: groupInfo.GroupName,
-		GroupId:   groupInfo.Id,
+		Status:      uint32(ackPackage.Status),
+		GroupName:   groupInfo.GroupName,
+		GroupId:     groupInfo.Id,
+		DestChainId: uint32(appCtx.SrcChainId),
 	}); err != nil {
 		return sdk.ExecuteResult{
 			Err: err,
@@ -305,9 +293,10 @@ func (app *GroupApp) handleMirrorGroupFailAckPackage(ctx sdk.Context, appCtx *sd
 	app.storageKeeper.SetGroupInfo(ctx, groupInfo)
 
 	if err := ctx.EventManager().EmitTypedEvents(&types.EventMirrorGroupResult{
-		Status:    uint32(types.StatusFail),
-		GroupName: groupInfo.GroupName,
-		GroupId:   groupInfo.Id,
+		Status:      uint32(types.StatusFail),
+		GroupName:   groupInfo.GroupName,
+		GroupId:     groupInfo.Id,
+		DestChainId: uint32(appCtx.SrcChainId),
 	}); err != nil {
 		return sdk.ExecuteResult{
 			Err: err,
diff --git a/x/storage/keeper/cross_app_object.go b/x/storage/keeper/cross_app_object.go
index da7729bc5..43b540e34 100644
--- a/x/storage/keeper/cross_app_object.go
+++ b/x/storage/keeper/cross_app_object.go
@@ -4,7 +4,6 @@ import (
 	"encoding/hex"
 
 	"cosmossdk.io/math"
-	"github.com/cosmos/cosmos-sdk/bsc/rlp"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 
 	"github.com/bnb-chain/greenfield/x/storage/types"
@@ -48,11 +47,7 @@ func (app *ObjectApp) ExecuteAckPackage(ctx sdk.Context, appCtx *sdk.CrossChainA
 			OperationType: operationType,
 			Package:       result.Payload,
 		}
-		wrapPayloadBts, err := rlp.EncodeToBytes(wrapPayload)
-		if err != nil {
-			panic(err)
-		}
-		result.Payload = wrapPayloadBts
+		result.Payload = wrapPayload.MustSerialize()
 	}
 
 	return result
@@ -83,11 +78,7 @@ func (app *ObjectApp) ExecuteFailAckPackage(ctx sdk.Context, appCtx *sdk.CrossCh
 			OperationType: operationType,
 			Package:       result.Payload,
 		}
-		wrapPayloadBts, err := rlp.EncodeToBytes(wrapPayload)
-		if err != nil {
-			panic(err)
-		}
-		result.Payload = wrapPayloadBts
+		result.Payload = wrapPayload.MustSerialize()
 	}
 
 	return result
@@ -119,11 +110,7 @@ func (app *ObjectApp) ExecuteSynPackage(ctx sdk.Context, appCtx *sdk.CrossChainA
 			OperationType: operationType,
 			Package:       result.Payload,
 		}
-		wrapPayloadBts, err := rlp.EncodeToBytes(wrapPayload)
-		if err != nil {
-			panic(err)
-		}
-		result.Payload = wrapPayloadBts
+		result.Payload = wrapPayload.MustSerialize()
 	}
 
 	return result
@@ -148,10 +135,11 @@ func (app *ObjectApp) handleMirrorObjectAckPackage(ctx sdk.Context, appCtx *sdk.
 	}
 
 	if err := ctx.EventManager().EmitTypedEvents(&types.EventMirrorObjectResult{
-		Status:     uint32(ackPackage.Status),
-		BucketName: objectInfo.BucketName,
-		ObjectName: objectInfo.ObjectName,
-		ObjectId:   objectInfo.Id,
+		Status:      uint32(ackPackage.Status),
+		BucketName:  objectInfo.BucketName,
+		ObjectName:  objectInfo.ObjectName,
+		ObjectId:    objectInfo.Id,
+		DestChainId: uint32(appCtx.SrcChainId),
 	}); err != nil {
 		return sdk.ExecuteResult{
 			Err: err,
@@ -176,10 +164,11 @@ func (app *ObjectApp) handleMirrorObjectFailAckPackage(ctx sdk.Context, appCtx *
 	app.storageKeeper.SetObjectInfo(ctx, objectInfo)
 
 	if err := ctx.EventManager().EmitTypedEvents(&types.EventMirrorObjectResult{
-		Status:     uint32(types.StatusFail),
-		BucketName: objectInfo.BucketName,
-		ObjectName: objectInfo.ObjectName,
-		ObjectId:   objectInfo.Id,
+		Status:      uint32(types.StatusFail),
+		BucketName:  objectInfo.BucketName,
+		ObjectName:  objectInfo.ObjectName,
+		ObjectId:    objectInfo.Id,
+		DestChainId: uint32(appCtx.SrcChainId),
 	}); err != nil {
 		return sdk.ExecuteResult{
 			Err: err,
diff --git a/x/storage/keeper/grpc_query.go b/x/storage/keeper/grpc_query.go
index ccca636bc..6e9972a20 100644
--- a/x/storage/keeper/grpc_query.go
+++ b/x/storage/keeper/grpc_query.go
@@ -7,6 +7,7 @@ import (
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
 
+	types2 "github.com/bnb-chain/greenfield/x/sp/types"
 	"github.com/bnb-chain/greenfield/x/storage/types"
 )
 
@@ -41,3 +42,32 @@ func (k Keeper) QueryParamsByTimestamp(c context.Context, req *types.QueryParams
 
 	return &types.QueryParamsByTimestampResponse{Params: params}, nil
 }
+
+func (k Keeper) QueryLockFee(c context.Context, req *types.QueryLockFeeRequest) (*types.QueryLockFeeResponse, error) {
+	if req == nil {
+		return nil, status.Error(codes.InvalidArgument, "invalid request")
+	}
+	ctx := sdk.UnwrapSDKContext(c)
+
+	createAt := req.GetCreateAt()
+	if createAt == 0 {
+		createAt = ctx.BlockTime().Unix()
+	}
+
+	primaryAcc, err := sdk.AccAddressFromHexUnsafe(req.PrimarySpAddress)
+	if err != nil {
+		return nil, status.Error(codes.InvalidArgument, "invalid primary storage provider address")
+	}
+
+	sp, found := k.spKeeper.GetStorageProviderByOperatorAddr(ctx, primaryAcc)
+	if !found {
+		return nil, types2.ErrStorageProviderNotFound
+	}
+
+	amount, err := k.GetObjectLockFee(ctx, sp.GetId(), createAt, req.PayloadSize)
+	if err != nil {
+		return nil, status.Error(codes.Internal, err.Error())
+	}
+
+	return &types.QueryLockFeeResponse{Amount: amount}, nil
+}
diff --git a/x/storage/keeper/grpc_query_test.go b/x/storage/keeper/grpc_query_test.go
index 186289f7e..05c1199b7 100644
--- a/x/storage/keeper/grpc_query_test.go
+++ b/x/storage/keeper/grpc_query_test.go
@@ -33,6 +33,7 @@ func makeKeeper(t *testing.T) (*keeper.Keeper, sdk.Context) {
 		&types.MockPaymentKeeper{},
 		&types.MockPermissionKeeper{},
 		&types.MockCrossChainKeeper{},
+		&types.MockVirtualGroupKeeper{},
 		authtypes.NewModuleAddress(govtypes.ModuleName).String(),
 	)
 
diff --git a/x/storage/keeper/keeper.go b/x/storage/keeper/keeper.go
index 796c67a4c..1bd440a11 100644
--- a/x/storage/keeper/keeper.go
+++ b/x/storage/keeper/keeper.go
@@ -14,27 +14,31 @@ import (
 	"github.com/cosmos/gogoproto/proto"
 
 	"github.com/bnb-chain/greenfield/internal/sequence"
+	gnfdtypes "github.com/bnb-chain/greenfield/types"
+	"github.com/bnb-chain/greenfield/types/common"
 	"github.com/bnb-chain/greenfield/types/resource"
 	permtypes "github.com/bnb-chain/greenfield/x/permission/types"
 	sptypes "github.com/bnb-chain/greenfield/x/sp/types"
 	"github.com/bnb-chain/greenfield/x/storage/types"
+	virtualgroupmoduletypes "github.com/bnb-chain/greenfield/x/virtualgroup/types"
 )
 
 type (
 	Keeper struct {
-		cdc              codec.BinaryCodec
-		storeKey         storetypes.StoreKey
-		tStoreKey        storetypes.StoreKey
-		spKeeper         types.SpKeeper
-		paymentKeeper    types.PaymentKeeper
-		accountKeeper    types.AccountKeeper
-		permKeeper       types.PermissionKeeper
-		crossChainKeeper types.CrossChainKeeper
+		cdc                codec.BinaryCodec
+		storeKey           storetypes.StoreKey
+		tStoreKey          storetypes.StoreKey
+		spKeeper           types.SpKeeper
+		paymentKeeper      types.PaymentKeeper
+		accountKeeper      types.AccountKeeper
+		permKeeper         types.PermissionKeeper
+		crossChainKeeper   types.CrossChainKeeper
+		virtualGroupKeeper types.VirtualGroupKeeper
 
 		// sequence
-		bucketSeq sequence.U256
-		objectSeq sequence.U256
-		groupSeq  sequence.U256
+		bucketSeq sequence.Sequence[sdkmath.Uint]
+		objectSeq sequence.Sequence[sdkmath.Uint]
+		groupSeq  sequence.Sequence[sdkmath.Uint]
 
 		authority string
 	}
@@ -49,24 +53,26 @@ func NewKeeper(
 	paymentKeeper types.PaymentKeeper,
 	permKeeper types.PermissionKeeper,
 	crossChainKeeper types.CrossChainKeeper,
+	virtualGroupKeeper types.VirtualGroupKeeper,
 	authority string,
 ) *Keeper {
 
 	k := Keeper{
-		cdc:              cdc,
-		storeKey:         storeKey,
-		tStoreKey:        tStoreKey,
-		accountKeeper:    accountKeeper,
-		spKeeper:         spKeeper,
-		paymentKeeper:    paymentKeeper,
-		permKeeper:       permKeeper,
-		crossChainKeeper: crossChainKeeper,
-		authority:        authority,
-	}
-
-	k.bucketSeq = sequence.NewSequence256(types.BucketSequencePrefix)
-	k.objectSeq = sequence.NewSequence256(types.ObjectSequencePrefix)
-	k.groupSeq = sequence.NewSequence256(types.GroupSequencePrefix)
+		cdc:                cdc,
+		storeKey:           storeKey,
+		tStoreKey:          tStoreKey,
+		accountKeeper:      accountKeeper,
+		spKeeper:           spKeeper,
+		paymentKeeper:      paymentKeeper,
+		permKeeper:         permKeeper,
+		crossChainKeeper:   crossChainKeeper,
+		virtualGroupKeeper: virtualGroupKeeper,
+		authority:          authority,
+	}
+
+	k.bucketSeq = sequence.NewSequence[sdkmath.Uint](types.BucketSequencePrefix)
+	k.objectSeq = sequence.NewSequence[sdkmath.Uint](types.ObjectSequencePrefix)
+	k.groupSeq = sequence.NewSequence[sdkmath.Uint](types.GroupSequencePrefix)
 	return &k
 }
 
@@ -95,33 +101,43 @@ func (k Keeper) CreateBucket(
 		return sdkmath.ZeroUint(), err
 	}
 
+	// check sp and its status
+	sp, found := k.spKeeper.GetStorageProviderByOperatorAddr(ctx, primarySpAcc)
+	if !found || sp.Status != sptypes.STATUS_IN_SERVICE {
+		return sdkmath.ZeroUint(), errors.Wrap(types.ErrNoSuchStorageProvider, "the storage provider is not exist or not in service")
+	}
+
 	// check primary sp approval
 	if opts.PrimarySpApproval.ExpiredHeight < uint64(ctx.BlockHeight()) {
 		return sdkmath.ZeroUint(), errors.Wrapf(types.ErrInvalidApproval, "The approval of sp is expired.")
 	}
-
-	if !ctx.IsCheckTx() {
-		err = k.VerifySPAndSignature(ctx, primarySpAcc, opts.ApprovalMsgBytes, opts.PrimarySpApproval.Sig)
-		if err != nil {
-			return sdkmath.ZeroUint(), err
-		}
+	err = k.VerifySPAndSignature(ctx, sp.Id, opts.ApprovalMsgBytes, opts.PrimarySpApproval.Sig)
+	if err != nil {
+		return sdkmath.ZeroUint(), err
+	}
+	gvgFamily, err := k.virtualGroupKeeper.GetAndCheckGVGFamilyAvailableForNewBucket(ctx, sp.Id, opts.PrimarySpApproval.GlobalVirtualGroupFamilyId)
+	if err != nil {
+		return sdkmath.ZeroUint(), err
 	}
 
 	bucketInfo := types.BucketInfo{
-		Owner:            ownerAcc.String(),
-		BucketName:       bucketName,
-		Visibility:       opts.Visibility,
-		CreateAt:         ctx.BlockTime().Unix(),
-		SourceType:       opts.SourceType,
-		BucketStatus:     types.BUCKET_STATUS_CREATED,
-		ChargedReadQuota: opts.ChargedReadQuota,
-		PaymentAddress:   paymentAcc.String(),
-		PrimarySpAddress: primarySpAcc.String(),
+		Owner:                      ownerAcc.String(),
+		BucketName:                 bucketName,
+		Visibility:                 opts.Visibility,
+		CreateAt:                   ctx.BlockTime().Unix(),
+		SourceType:                 opts.SourceType,
+		BucketStatus:               types.BUCKET_STATUS_CREATED,
+		ChargedReadQuota:           opts.ChargedReadQuota,
+		PaymentAddress:             paymentAcc.String(),
+		PrimarySpId:                sp.Id,
+		GlobalVirtualGroupFamilyId: gvgFamily.Id,
 	}
 
+	internalBucketInfo := types.InternalBucketInfo{PriceTime: ctx.BlockTime().Unix()}
+
 	// charge by read quota
 	if opts.ChargedReadQuota != 0 {
-		err = k.ChargeInitialReadFee(ctx, &bucketInfo)
+		err = k.ChargeBucketReadFee(ctx, &bucketInfo, &internalBucketInfo)
 		if err != nil {
 			return sdkmath.ZeroUint(), err
 		}
@@ -132,21 +148,23 @@ func (k Keeper) CreateBucket(
 
 	// store the bucket
 	bz := k.cdc.MustMarshal(&bucketInfo)
-	store.Set(bucketKey, sequence.EncodeSequence(bucketInfo.Id))
+	store.Set(bucketKey, k.bucketSeq.EncodeSequence(bucketInfo.Id))
 	store.Set(types.GetBucketByIDKey(bucketInfo.Id), bz)
+	k.SetInternalBucketInfo(ctx, bucketInfo.Id, &internalBucketInfo)
 
 	// emit CreateBucket Event
 	if err = ctx.EventManager().EmitTypedEvents(&types.EventCreateBucket{
-		Owner:            bucketInfo.Owner,
-		BucketName:       bucketInfo.BucketName,
-		Visibility:       bucketInfo.Visibility,
-		CreateAt:         bucketInfo.CreateAt,
-		BucketId:         bucketInfo.Id,
-		SourceType:       bucketInfo.SourceType,
-		Status:           bucketInfo.BucketStatus,
-		ChargedReadQuota: bucketInfo.ChargedReadQuota,
-		PaymentAddress:   bucketInfo.PaymentAddress,
-		PrimarySpAddress: bucketInfo.PrimarySpAddress,
+		Owner:                      bucketInfo.Owner,
+		BucketName:                 bucketInfo.BucketName,
+		Visibility:                 bucketInfo.Visibility,
+		CreateAt:                   bucketInfo.CreateAt,
+		BucketId:                   bucketInfo.Id,
+		SourceType:                 bucketInfo.SourceType,
+		Status:                     bucketInfo.BucketStatus,
+		ChargedReadQuota:           bucketInfo.ChargedReadQuota,
+		PaymentAddress:             bucketInfo.PaymentAddress,
+		PrimarySpId:                bucketInfo.PrimarySpId,
+		GlobalVirtualGroupFamilyId: bucketInfo.GlobalVirtualGroupFamilyId,
 	}); err != nil {
 		return sdkmath.Uint{}, err
 	}
@@ -174,10 +192,18 @@ func (k Keeper) DeleteBucket(ctx sdk.Context, operator sdk.AccAddress, bucketNam
 		return types.ErrBucketNotEmpty
 	}
 
+	// check lvgs
+	internalBucketInfo := k.MustGetInternalBucketInfo(ctx, bucketInfo.Id)
+	for _, lvg := range internalBucketInfo.LocalVirtualGroups {
+		if lvg.StoredSize != 0 || lvg.TotalChargeSize != 0 {
+			return types.ErrVirtualGroupOperateFailed.Wrapf("non-empty lvg, %s", lvg.String())
+		}
+	}
+
 	// change the bill
-	err := k.ChargeDeleteBucket(ctx, bucketInfo)
+	err := k.UnChargeBucketReadFee(ctx, bucketInfo, internalBucketInfo)
 	if err != nil {
-		return types.ErrChargeFailed.Wrapf("ChargeDeleteBucket error: %s", err)
+		return types.ErrChargeFailed.Wrapf("cancel charge bucket read fee error: %s", err)
 	}
 
 	return k.doDeleteBucket(ctx, operator, bucketInfo)
@@ -188,16 +214,19 @@ func (k Keeper) doDeleteBucket(ctx sdk.Context, operator sdk.AccAddress, bucketI
 	store.Delete(types.GetBucketKey(bucketInfo.BucketName))
 	store.Delete(types.GetBucketByIDKey(bucketInfo.Id))
 	store.Delete(types.GetQuotaKey(bucketInfo.Id))
+	store.Delete(types.GetInternalBucketInfoKey(bucketInfo.Id))
+	store.Delete(types.GetMigrationBucketKey(bucketInfo.Id))
 
-	if err := k.appendResourceIdForGarbageCollection(ctx, resource.RESOURCE_TYPE_BUCKET, bucketInfo.Id); err != nil {
+	err := k.appendResourceIdForGarbageCollection(ctx, resource.RESOURCE_TYPE_BUCKET, bucketInfo.Id)
+	if err != nil {
 		return err
 	}
-	err := ctx.EventManager().EmitTypedEvents(&types.EventDeleteBucket{
-		Operator:         operator.String(),
-		Owner:            bucketInfo.Owner,
-		BucketName:       bucketInfo.BucketName,
-		BucketId:         bucketInfo.Id,
-		PrimarySpAddress: bucketInfo.PrimarySpAddress,
+	err = ctx.EventManager().EmitTypedEvents(&types.EventDeleteBucket{
+		Operator:    operator.String(),
+		Owner:       bucketInfo.Owner,
+		BucketName:  bucketInfo.BucketName,
+		BucketId:    bucketInfo.Id,
+		PrimarySpId: bucketInfo.PrimarySpId,
 	})
 	return err
 }
@@ -212,12 +241,15 @@ func (k Keeper) ForceDeleteBucket(ctx sdk.Context, bucketId sdkmath.Uint, cap ui
 	}
 
 	bucketDeleted := false
-	sp := sdk.MustAccAddressFromHex(bucketInfo.PrimarySpAddress)
+
+	sp := k.spKeeper.MustGetStorageProvider(ctx, bucketInfo.PrimarySpId)
+	spOperatorAddr := sdk.MustAccAddressFromHex(sp.OperatorAddress)
 
 	store := ctx.KVStore(k.storeKey)
 	objectPrefixStore := prefix.NewStore(store, types.GetObjectKeyOnlyBucketPrefix(bucketInfo.BucketName))
 	iter := objectPrefixStore.Iterator(nil, nil)
 	defer iter.Close()
+	u256Seq := sequence.Sequence[sdkmath.Uint]{}
 
 	var err error
 	deleted := uint64(0) // deleted object count
@@ -226,7 +258,7 @@ func (k Keeper) ForceDeleteBucket(ctx sdk.Context, bucketId sdkmath.Uint, cap ui
 			return false, deleted, nil // break is also fine here
 		}
 
-		bz := store.Get(types.GetObjectByIDKey(types.DecodeSequence(iter.Value())))
+		bz := store.Get(types.GetObjectByIDKey(u256Seq.DecodeSequence(iter.Value())))
 		if bz == nil {
 			panic("should not happen")
 		}
@@ -250,30 +282,33 @@ func (k Keeper) ForceDeleteBucket(ctx sdk.Context, bucketId sdkmath.Uint, cap ui
 		}
 
 		if objectStatus == types.OBJECT_STATUS_CREATED {
-			if err = k.UnlockStoreFee(ctx, bucketInfo, &objectInfo); err != nil {
+			if err = k.UnlockObjectStoreFee(ctx, bucketInfo, &objectInfo); err != nil {
 				ctx.Logger().Error("unlock store fee error", "err", err)
 				return false, deleted, err
 			}
 		} else if objectStatus == types.OBJECT_STATUS_SEALED {
-			if err = k.ChargeDeleteObject(ctx, bucketInfo, &objectInfo); err != nil {
+			internalBucketInfo := k.MustGetInternalBucketInfo(ctx, bucketInfo.Id)
+			if err = k.UnChargeObjectStoreFee(ctx, bucketInfo, internalBucketInfo, &objectInfo); err != nil {
 				ctx.Logger().Error("charge delete object error", "err", err)
 				return false, deleted, err
 			}
+			k.SetInternalBucketInfo(ctx, bucketInfo.Id, internalBucketInfo)
 		}
-		if err = k.doDeleteObject(ctx, sp, bucketInfo, &objectInfo); err != nil {
-			ctx.Logger().Error("do delete object error", "err", err)
+		if err := k.doDeleteObject(ctx, spOperatorAddr, bucketInfo, &objectInfo); err != nil {
+			ctx.Logger().Error("do delete object err", "err", err)
 			return false, deleted, err
 		}
 		deleted++
 	}
 
 	if !iter.Valid() {
-		if err = k.ChargeDeleteBucket(ctx, bucketInfo); err != nil {
+		internalBucketInfo := k.MustGetInternalBucketInfo(ctx, bucketInfo.Id)
+		if err = k.UnChargeBucketReadFee(ctx, bucketInfo, internalBucketInfo); err != nil {
 			ctx.Logger().Error("charge delete bucket error", "err", err)
 			return false, deleted, err
 		}
 
-		if err = k.doDeleteBucket(ctx, sp, bucketInfo); err != nil {
+		if err := k.doDeleteBucket(ctx, spOperatorAddr, bucketInfo); err != nil {
 			ctx.Logger().Error("do delete bucket error", "err", err)
 			return false, deleted, err
 		}
@@ -335,7 +370,9 @@ func (k Keeper) UpdateBucketInfo(ctx sdk.Context, operator sdk.AccAddress, bucke
 	} else {
 		paymentAcc = sdk.MustAccAddressFromHex(bucketInfo.PaymentAddress)
 	}
-	err = k.UpdateBucketInfoAndCharge(ctx, bucketInfo, paymentAcc.String(), *opts.ChargedReadQuota)
+
+	internalBucketInfo := k.MustGetInternalBucketInfo(ctx, bucketInfo.Id)
+	err = k.UpdateBucketInfoAndCharge(ctx, bucketInfo, internalBucketInfo, paymentAcc.String(), *opts.ChargedReadQuota)
 	if err != nil {
 		return err
 	}
@@ -343,6 +380,7 @@ func (k Keeper) UpdateBucketInfo(ctx sdk.Context, operator sdk.AccAddress, bucke
 	store := ctx.KVStore(k.storeKey)
 	bz := k.cdc.MustMarshal(bucketInfo)
 	store.Set(types.GetBucketByIDKey(bucketInfo.Id), bz)
+	k.SetInternalBucketInfo(ctx, bucketInfo.Id, internalBucketInfo)
 
 	if err := ctx.EventManager().EmitTypedEvents(&types.EventUpdateBucketInfo{
 		Operator:               operator.String(),
@@ -376,8 +414,9 @@ func (k Keeper) DiscontinueBucket(ctx sdk.Context, operator sdk.AccAddress, buck
 		return types.ErrInvalidBucketStatus
 	}
 
-	if !sdk.MustAccAddressFromHex(sp.OperatorAddress).Equals(sdk.MustAccAddressFromHex(bucketInfo.PrimarySpAddress)) {
-		return errors.Wrapf(types.ErrAccessDenied, "only primary sp is allowed to do discontinue bucket")
+	if sp.Id != bucketInfo.PrimarySpId {
+		return errors.Wrapf(types.ErrAccessDenied,
+			"only primary sp is allowed to do discontinue bucket, expect sp id : %d", bucketInfo.PrimarySpId)
 	}
 
 	count := k.getDiscontinueBucketCount(ctx, operator)
@@ -424,7 +463,7 @@ func (k Keeper) GetBucketInfo(ctx sdk.Context, bucketName string) (*types.Bucket
 		return nil, false
 	}
 
-	return k.GetBucketInfoById(ctx, sequence.DecodeSequence(bz))
+	return k.GetBucketInfoById(ctx, k.bucketSeq.DecodeSequence(bz))
 }
 
 func (k Keeper) GetBucketInfoById(ctx sdk.Context, bucketId sdkmath.Uint) (*types.BucketInfo, bool) {
@@ -456,8 +495,9 @@ func (k Keeper) CreateObject(
 	if !found {
 		return sdkmath.ZeroUint(), types.ErrNoSuchBucket
 	}
-	if bucketInfo.BucketStatus == types.BUCKET_STATUS_DISCONTINUED {
-		return sdkmath.ZeroUint(), types.ErrBucketDiscontinued
+	err := bucketInfo.CheckBucketStatus()
+	if err != nil {
+		return sdkmath.ZeroUint(), err
 	}
 
 	// verify permission
@@ -470,20 +510,10 @@ func (k Keeper) CreateObject(
 			operator.String(), bucketName)
 	}
 
-	// check secondary sps
-	var secondarySPs []string
-	for _, sp := range opts.SecondarySpAddresses {
-		spAcc := sdk.MustAccAddressFromHex(sp)
-		err := k.spKeeper.IsStorageProviderExistAndInService(ctx, spAcc)
-		if err != nil {
-			return sdkmath.ZeroUint(), err
-		}
-		secondarySPs = append(secondarySPs, spAcc.String())
-	}
-
 	// We use the last address in SecondarySpAddresses to store the creator so that it can be identified when canceling create
+	var creator sdk.AccAddress
 	if !operator.Equals(sdk.MustAccAddressFromHex(bucketInfo.Owner)) {
-		secondarySPs = append(secondarySPs, operator.String())
+		creator = operator
 	}
 
 	// check approval
@@ -491,13 +521,9 @@ func (k Keeper) CreateObject(
 		return sdkmath.ZeroUint(), errors.Wrapf(types.ErrInvalidApproval, "The approval of sp is expired.")
 	}
 
-	var err error
-	if !ctx.IsCheckTx() { // no signature verification for simulation
-		err = k.VerifySPAndSignature(ctx, sdk.MustAccAddressFromHex(bucketInfo.PrimarySpAddress), opts.ApprovalMsgBytes,
-			opts.PrimarySpApproval.Sig)
-		if err != nil {
-			return sdkmath.ZeroUint(), err
-		}
+	err = k.VerifySPAndSignature(ctx, bucketInfo.PrimarySpId, opts.ApprovalMsgBytes, opts.PrimarySpApproval.Sig)
+	if err != nil {
+		return sdkmath.ZeroUint(), err
 	}
 
 	objectKey := types.GetObjectKey(bucketName, objectName)
@@ -516,30 +542,29 @@ func (k Keeper) CreateObject(
 
 	// construct objectInfo
 	objectInfo := types.ObjectInfo{
-		Owner:                bucketInfo.Owner,
-		BucketName:           bucketName,
-		ObjectName:           objectName,
-		PayloadSize:          payloadSize,
-		Visibility:           opts.Visibility,
-		ContentType:          opts.ContentType,
-		Id:                   k.GenNextObjectID(ctx),
-		CreateAt:             ctx.BlockTime().Unix(),
-		ObjectStatus:         objectStatus,
-		RedundancyType:       opts.RedundancyType,
-		SourceType:           opts.SourceType,
-		Checksums:            opts.Checksums,
-		SecondarySpAddresses: secondarySPs,
+		Owner:          bucketInfo.Owner,
+		Creator:        creator.String(),
+		BucketName:     bucketName,
+		ObjectName:     objectName,
+		PayloadSize:    payloadSize,
+		Visibility:     opts.Visibility,
+		ContentType:    opts.ContentType,
+		Id:             k.GenNextObjectID(ctx),
+		CreateAt:       ctx.BlockTime().Unix(),
+		ObjectStatus:   objectStatus,
+		RedundancyType: opts.RedundancyType,
+		SourceType:     opts.SourceType,
+		Checksums:      opts.Checksums,
 	}
 
 	if objectInfo.PayloadSize == 0 {
-		// charge directly without lock charge
-		err = k.ChargeStoreFee(ctx, bucketInfo, &objectInfo)
+		_, err := k.SealEmptyObjectOnVirtualGroup(ctx, bucketInfo, &objectInfo)
 		if err != nil {
 			return sdkmath.ZeroUint(), err
 		}
 	} else {
 		// Lock Fee
-		err = k.LockStoreFee(ctx, bucketInfo, &objectInfo)
+		err = k.LockObjectStoreFee(ctx, bucketInfo, &objectInfo)
 		if err != nil {
 			return sdkmath.ZeroUint(), err
 		}
@@ -549,25 +574,25 @@ func (k Keeper) CreateObject(
 	store.Set(types.GetBucketByIDKey(bucketInfo.Id), bbz)
 
 	obz := k.cdc.MustMarshal(&objectInfo)
-	store.Set(objectKey, sequence.EncodeSequence(objectInfo.Id))
+	store.Set(objectKey, k.objectSeq.EncodeSequence(objectInfo.Id))
 	store.Set(types.GetObjectByIDKey(objectInfo.Id), obz)
 
 	if err = ctx.EventManager().EmitTypedEvents(&types.EventCreateObject{
-		Creator:          operator.String(),
-		Owner:            objectInfo.Owner,
-		BucketName:       bucketInfo.BucketName,
-		ObjectName:       objectInfo.ObjectName,
-		BucketId:         bucketInfo.Id,
-		ObjectId:         objectInfo.Id,
-		CreateAt:         objectInfo.CreateAt,
-		PayloadSize:      objectInfo.PayloadSize,
-		Visibility:       objectInfo.Visibility,
-		PrimarySpAddress: bucketInfo.PrimarySpAddress,
-		ContentType:      objectInfo.ContentType,
-		Status:           objectInfo.ObjectStatus,
-		RedundancyType:   objectInfo.RedundancyType,
-		SourceType:       objectInfo.SourceType,
-		Checksums:        objectInfo.Checksums,
+		Creator:        operator.String(),
+		Owner:          objectInfo.Owner,
+		BucketName:     bucketInfo.BucketName,
+		ObjectName:     objectInfo.ObjectName,
+		BucketId:       bucketInfo.Id,
+		ObjectId:       objectInfo.Id,
+		CreateAt:       objectInfo.CreateAt,
+		PayloadSize:    objectInfo.PayloadSize,
+		Visibility:     objectInfo.Visibility,
+		PrimarySpId:    bucketInfo.PrimarySpId,
+		ContentType:    objectInfo.ContentType,
+		Status:         objectInfo.ObjectStatus,
+		RedundancyType: objectInfo.RedundancyType,
+		SourceType:     objectInfo.SourceType,
+		Checksums:      objectInfo.Checksums,
 	}); err != nil {
 		return objectInfo.Id, err
 	}
@@ -596,7 +621,7 @@ func (k Keeper) GetObjectInfo(ctx sdk.Context, bucketName string, objectName str
 		return nil, false
 	}
 
-	return k.GetObjectInfoById(ctx, sequence.DecodeSequence(bz))
+	return k.GetObjectInfoById(ctx, k.objectSeq.DecodeSequence(bz))
 }
 
 func (k Keeper) GetObjectInfoById(ctx sdk.Context, objectId sdkmath.Uint) (*types.ObjectInfo, bool) {
@@ -613,8 +638,8 @@ func (k Keeper) GetObjectInfoById(ctx sdk.Context, objectId sdkmath.Uint) (*type
 }
 
 type SealObjectOptions struct {
-	SecondarySpAddresses  []string
-	SecondarySpSignatures [][]byte
+	GlobalVirtualGroupId     uint32
+	SecondarySpBlsSignatures []byte
 }
 
 func (k Keeper) SealObject(
@@ -631,7 +656,7 @@ func (k Keeper) SealObject(
 		return errors.Wrapf(types.ErrNoSuchStorageProvider, "SP seal address: %s", spSealAcc.String())
 	}
 
-	if !sdk.MustAccAddressFromHex(sp.OperatorAddress).Equals(sdk.MustAccAddressFromHex(bucketInfo.PrimarySpAddress)) {
+	if sp.Id != bucketInfo.PrimarySpId {
 		return errors.Wrapf(types.ErrAccessDenied, "Only SP's seal address is allowed to SealObject")
 	}
 
@@ -644,26 +669,32 @@ func (k Keeper) SealObject(
 		return types.ErrObjectAlreadySealed
 	}
 
-	// check the signature of secondary sps
-	// SecondarySP signs the root hash(checksum) of all pieces stored on it, and needs to verify that the signature here.
-	var secondarySps []string
-	for i, spAddr := range opts.SecondarySpAddresses {
-		spAcc := sdk.MustAccAddressFromHex(spAddr)
-		secondarySps = append(secondarySps, spAcc.String())
-		sr := types.NewSecondarySpSignDoc(spAcc, objectInfo.Id, objectInfo.Checksums[i+1])
-		err := k.VerifySPAndSignature(ctx, spAcc, sr.GetSignBytes(), opts.SecondarySpSignatures[i])
-		if err != nil {
-			return err
-		}
+	gvg, found := k.virtualGroupKeeper.GetGVG(ctx, opts.GlobalVirtualGroupId)
+	if !found {
+		return virtualgroupmoduletypes.ErrGVGNotExist
+	}
+
+	if gvg.FamilyId != bucketInfo.GlobalVirtualGroupFamilyId || gvg.PrimarySpId != bucketInfo.PrimarySpId {
+		return types.ErrInvalidGlobalVirtualGroup.Wrapf("Global virtual group mismatch, familyID: %d, bucket family ID: %d", gvg.FamilyId, bucketInfo.GlobalVirtualGroupFamilyId)
 	}
-	objectInfo.SecondarySpAddresses = secondarySps
 
-	// unlock and charge store fee
-	err := k.UnlockAndChargeStoreFee(ctx, bucketInfo, objectInfo)
+	expectSecondarySPNum := k.GetExpectSecondarySPNumForECObject(ctx, objectInfo.CreateAt)
+	if int(expectSecondarySPNum) != len(gvg.SecondarySpIds) {
+		return types.ErrInvalidGlobalVirtualGroup.Wrapf("secondary sp num mismatch, expect (%d), but (%d)",
+			expectSecondarySPNum, len(gvg.SecondarySpIds))
+	}
+	// validate seal object bls aggregated sig from secondary sps
+	secondarySpsSealObjectBlsSignHash := types.NewSecondarySpSealObjectSignDoc(ctx.ChainID(), gvg.Id, objectInfo.Id, types.GenerateHash(objectInfo.Checksums[:])).GetBlsSignHash()
+	err := k.VerifyGVGSecondarySPsBlsSignature(ctx, gvg, secondarySpsSealObjectBlsSignHash, opts.SecondarySpBlsSignatures)
 	if err != nil {
 		return err
 	}
 
+	_, err = k.SealObjectOnVirtualGroup(ctx, bucketInfo, opts.GlobalVirtualGroupId, objectInfo)
+	if err != nil {
+		return errors.Wrapf(types.ErrInvalidGlobalVirtualGroup, "err message: %s", err)
+	}
+
 	objectInfo.ObjectStatus = types.OBJECT_STATUS_SEALED
 
 	store := ctx.KVStore(k.storeKey)
@@ -679,7 +710,8 @@ func (k Keeper) SealObject(
 		ObjectName:           objectInfo.ObjectName,
 		ObjectId:             objectInfo.Id,
 		Status:               objectInfo.ObjectStatus,
-		SecondarySpAddresses: objectInfo.SecondarySpAddresses,
+		GlobalVirtualGroupId: opts.GlobalVirtualGroupId,
+		LocalVirtualGroupId:  objectInfo.LocalVirtualGroupId,
 	}); err != nil {
 		return err
 	}
@@ -708,18 +740,15 @@ func (k Keeper) CancelCreateObject(
 	}
 
 	var creator sdk.AccAddress
-	// We use the last address in SecondarySpAddresses to store the creator so that it can be identified when canceling create
-	// if the operator is not the creator, we should return access deny
-	if len(objectInfo.SecondarySpAddresses) >= 1 {
-		creator = sdk.MustAccAddressFromHex(objectInfo.SecondarySpAddresses[len(objectInfo.SecondarySpAddresses)-1])
-	}
-	// By default, the creator is the owner
 	owner := sdk.MustAccAddressFromHex(objectInfo.Owner)
-	if !operator.Equals(creator) && !operator.Equals(owner) {
+	if objectInfo.Creator != "" {
+		creator = sdk.MustAccAddressFromHex(objectInfo.Creator)
+	}
+	if !operator.Equals(owner) && !operator.Equals(creator) {
 		return errors.Wrapf(types.ErrAccessDenied, "Only allowed owner/creator to do cancel create object")
 	}
 
-	err := k.UnlockStoreFee(ctx, bucketInfo, objectInfo)
+	err := k.UnlockObjectStoreFee(ctx, bucketInfo, objectInfo)
 	if err != nil {
 		return err
 	}
@@ -731,11 +760,11 @@ func (k Keeper) CancelCreateObject(
 	store.Delete(types.GetObjectByIDKey(objectInfo.Id))
 
 	if err := ctx.EventManager().EmitTypedEvents(&types.EventCancelCreateObject{
-		Operator:         operator.String(),
-		BucketName:       bucketInfo.BucketName,
-		ObjectName:       objectInfo.ObjectName,
-		PrimarySpAddress: bucketInfo.PrimarySpAddress,
-		ObjectId:         objectInfo.Id,
+		Operator:    operator.String(),
+		BucketName:  bucketInfo.BucketName,
+		ObjectName:  objectInfo.ObjectName,
+		ObjectId:    objectInfo.Id,
+		PrimarySpId: bucketInfo.PrimarySpId,
 	}); err != nil {
 		return err
 	}
@@ -772,10 +801,12 @@ func (k Keeper) DeleteObject(
 			operator.String(), bucketName, objectName)
 	}
 
-	err := k.ChargeDeleteObject(ctx, bucketInfo, objectInfo)
+	internalBucketInfo := k.MustGetInternalBucketInfo(ctx, bucketInfo.Id)
+	err := k.UnChargeObjectStoreFee(ctx, bucketInfo, internalBucketInfo, objectInfo)
 	if err != nil {
 		return err
 	}
+	k.SetInternalBucketInfo(ctx, bucketInfo.Id, internalBucketInfo)
 
 	err = k.doDeleteObject(ctx, operator, bucketInfo, objectInfo)
 	if err != nil {
@@ -793,17 +824,22 @@ func (k Keeper) doDeleteObject(ctx sdk.Context, operator sdk.AccAddress, bucketI
 	store.Delete(types.GetObjectKey(bucketInfo.BucketName, objectInfo.ObjectName))
 	store.Delete(types.GetObjectByIDKey(objectInfo.Id))
 
-	if err := k.appendResourceIdForGarbageCollection(ctx, resource.RESOURCE_TYPE_OBJECT, objectInfo.Id); err != nil {
+	err := k.DeleteObjectFromVirtualGroup(ctx, bucketInfo, objectInfo)
+	if err != nil {
 		return err
 	}
 
-	err := ctx.EventManager().EmitTypedEvents(&types.EventDeleteObject{
-		Operator:             operator.String(),
-		BucketName:           bucketInfo.BucketName,
-		ObjectName:           objectInfo.ObjectName,
-		ObjectId:             objectInfo.Id,
-		PrimarySpAddress:     bucketInfo.PrimarySpAddress,
-		SecondarySpAddresses: objectInfo.SecondarySpAddresses,
+	err = k.appendResourceIdForGarbageCollection(ctx, resource.RESOURCE_TYPE_OBJECT, objectInfo.Id)
+	if err != nil {
+		return err
+	}
+
+	err = ctx.EventManager().EmitTypedEvents(&types.EventDeleteObject{
+		Operator:            operator.String(),
+		BucketName:          bucketInfo.BucketName,
+		ObjectName:          objectInfo.ObjectName,
+		ObjectId:            objectInfo.Id,
+		LocalVirtualGroupId: objectInfo.LocalVirtualGroupId,
 	})
 	return err
 }
@@ -825,23 +861,29 @@ func (k Keeper) ForceDeleteObject(ctx sdk.Context, objectId sdkmath.Uint) error
 		return err
 	}
 
+	sp, found := k.spKeeper.GetStorageProvider(ctx, bucketInfo.PrimarySpId)
+	if !found {
+		return sptypes.ErrStorageProviderNotFound
+	}
 	if objectStatus == types.OBJECT_STATUS_CREATED {
-		err := k.UnlockStoreFee(ctx, bucketInfo, objectInfo)
+		err := k.UnlockObjectStoreFee(ctx, bucketInfo, objectInfo)
 		if err != nil {
 			ctx.Logger().Error("unlock store fee error", "err", err)
 			return err
 		}
 	} else if objectStatus == types.OBJECT_STATUS_SEALED {
-		err := k.ChargeDeleteObject(ctx, bucketInfo, objectInfo)
+		internalBucketInfo := k.MustGetInternalBucketInfo(ctx, bucketInfo.Id)
+		err := k.UnChargeObjectStoreFee(ctx, bucketInfo, internalBucketInfo, objectInfo)
 		if err != nil {
 			ctx.Logger().Error("charge delete object error", "err", err)
 			return err
 		}
+		k.SetInternalBucketInfo(ctx, bucketInfo.Id, internalBucketInfo)
 	}
 
-	sp := sdk.MustAccAddressFromHex(bucketInfo.PrimarySpAddress)
-	err = k.doDeleteObject(ctx, sp, bucketInfo, objectInfo)
+	err = k.doDeleteObject(ctx, sdk.MustAccAddressFromHex(sp.OperatorAddress), bucketInfo, objectInfo)
 	if err != nil {
+		ctx.Logger().Error("do delete object err", "err", err)
 		return err
 	}
 	return nil
@@ -863,6 +905,11 @@ func (k Keeper) CopyObject(
 		return sdkmath.ZeroUint(), errors.Wrapf(types.ErrNoSuchBucket, "dst bucket name (%s)", dstBucketName)
 	}
 
+	err := dstBucketInfo.CheckBucketStatus()
+	if err != nil {
+		return sdkmath.ZeroUint(), err
+	}
+
 	srcObjectInfo, found := k.GetObjectInfo(ctx, srcBucketName, srcObjectName)
 	if !found {
 		return sdkmath.ZeroUint(), errors.Wrapf(types.ErrNoSuchObject, "src object name (%s)", srcObjectName)
@@ -884,9 +931,7 @@ func (k Keeper) CopyObject(
 		return sdkmath.ZeroUint(), errors.Wrapf(types.ErrInvalidApproval, "The approval of sp is expired.")
 	}
 
-	err := k.VerifySPAndSignature(ctx, sdk.MustAccAddressFromHex(dstBucketInfo.PrimarySpAddress),
-		opts.ApprovalMsgBytes,
-		opts.PrimarySpApproval.Sig)
+	err = k.VerifySPAndSignature(ctx, dstBucketInfo.PrimarySpId, opts.ApprovalMsgBytes, opts.PrimarySpApproval.Sig)
 	if err != nil {
 		return sdkmath.ZeroUint(), err
 	}
@@ -916,12 +961,12 @@ func (k Keeper) CopyObject(
 	}
 
 	if srcObjectInfo.PayloadSize == 0 {
-		err = k.ChargeStoreFee(ctx, dstBucketInfo, &objectInfo)
+		_, err := k.SealEmptyObjectOnVirtualGroup(ctx, dstBucketInfo, &objectInfo)
 		if err != nil {
 			return sdkmath.ZeroUint(), err
 		}
 	} else {
-		err = k.LockStoreFee(ctx, dstBucketInfo, &objectInfo)
+		err = k.LockObjectStoreFee(ctx, dstBucketInfo, &objectInfo)
 		if err != nil {
 			return sdkmath.ZeroUint(), err
 		}
@@ -931,7 +976,7 @@ func (k Keeper) CopyObject(
 	store.Set(types.GetBucketByIDKey(dstBucketInfo.Id), bbz)
 
 	obz := k.cdc.MustMarshal(&objectInfo)
-	store.Set(types.GetObjectKey(dstBucketName, dstObjectName), sequence.EncodeSequence(objectInfo.Id))
+	store.Set(types.GetObjectKey(dstBucketName, dstObjectName), k.objectSeq.EncodeSequence(objectInfo.Id))
 	store.Set(types.GetObjectByIDKey(objectInfo.Id), obz)
 
 	if err := ctx.EventManager().EmitTypedEvents(&types.EventCopyObject{
@@ -970,11 +1015,11 @@ func (k Keeper) RejectSealObject(ctx sdk.Context, operator sdk.AccAddress, bucke
 	if sp.Status != sptypes.STATUS_IN_SERVICE {
 		return sptypes.ErrStorageProviderNotInService
 	}
-	if !sdk.MustAccAddressFromHex(sp.OperatorAddress).Equals(sdk.MustAccAddressFromHex(bucketInfo.PrimarySpAddress)) {
+	if sp.Id != bucketInfo.PrimarySpId {
 		return errors.Wrapf(types.ErrAccessDenied, "Only allowed primary SP to do cancel create object")
 	}
 
-	err := k.UnlockStoreFee(ctx, bucketInfo, objectInfo)
+	err := k.UnlockObjectStoreFee(ctx, bucketInfo, objectInfo)
 	if err != nil {
 		return err
 	}
@@ -1014,7 +1059,7 @@ func (k Keeper) DiscontinueObject(ctx sdk.Context, operator sdk.AccAddress, buck
 		return types.ErrInvalidBucketStatus
 	}
 
-	if !sdk.MustAccAddressFromHex(sp.OperatorAddress).Equals(sdk.MustAccAddressFromHex(bucketInfo.PrimarySpAddress)) {
+	if sp.Id != bucketInfo.PrimarySpId {
 		return errors.Wrapf(types.ErrAccessDenied, "only primary sp is allowed to do discontinue objects")
 	}
 
@@ -1121,7 +1166,7 @@ func (k Keeper) CreateGroup(
 	}
 
 	gbz := k.cdc.MustMarshal(&groupInfo)
-	store.Set(groupKey, sequence.EncodeSequence(groupInfo.Id))
+	store.Set(groupKey, k.groupSeq.EncodeSequence(groupInfo.Id))
 	store.Set(types.GetGroupByIDKey(groupInfo.Id), gbz)
 
 	// need to limit the size of Msg.Members to avoid taking too long to execute the msg
@@ -1164,7 +1209,7 @@ func (k Keeper) GetGroupInfo(ctx sdk.Context, ownerAddr sdk.AccAddress,
 		return nil, false
 	}
 
-	return k.GetGroupInfoById(ctx, sequence.DecodeSequence(bz))
+	return k.GetGroupInfoById(ctx, k.groupSeq.DecodeSequence(bz))
 }
 
 func (k Keeper) GetGroupInfoById(ctx sdk.Context, groupId sdkmath.Uint) (*types.GroupInfo, bool) {
@@ -1323,10 +1368,10 @@ func (k Keeper) UpdateGroupExtra(ctx sdk.Context, operator sdk.AccAddress, group
 	return nil
 }
 
-func (k Keeper) VerifySPAndSignature(ctx sdk.Context, spAcc sdk.AccAddress, sigData []byte, signature []byte) error {
-	sp, found := k.spKeeper.GetStorageProvider(ctx, spAcc)
+func (k Keeper) VerifySPAndSignature(ctx sdk.Context, spID uint32, sigData []byte, signature []byte) error {
+	sp, found := k.spKeeper.GetStorageProvider(ctx, spID)
 	if !found {
-		return errors.Wrapf(types.ErrNoSuchStorageProvider, "SP operator address: %s", spAcc.String())
+		return errors.Wrapf(types.ErrNoSuchStorageProvider, "SP id: %d", spID)
 	}
 	if sp.Status != sptypes.STATUS_IN_SERVICE {
 		return sptypes.ErrStorageProviderNotInService
@@ -1334,7 +1379,7 @@ func (k Keeper) VerifySPAndSignature(ctx sdk.Context, spAcc sdk.AccAddress, sigD
 
 	approvalAccAddress := sdk.MustAccAddressFromHex(sp.ApprovalAddress)
 
-	err := types.VerifySignature(approvalAccAddress, sdk.Keccak256(sigData), signature)
+	err := gnfdtypes.VerifySignature(approvalAccAddress, sdk.Keccak256(sigData), signature)
 	if err != nil {
 		return errors.Wrapf(types.ErrInvalidApproval, "verify signature error: %s", err)
 	}
@@ -1436,7 +1481,7 @@ func (k Keeper) DeleteDiscontinueObjectsUntil(ctx sdk.Context, timestamp int64,
 
 			err = k.ForceDeleteObject(ctx, id)
 			if err != nil {
-				ctx.Logger().Error("delete object error", "err", err, "height", ctx.BlockHeight())
+				ctx.Logger().Error("delete object error", "err", err, "id", id, "height", ctx.BlockHeight())
 				return deleted, err
 			}
 			deleted++
@@ -1518,7 +1563,7 @@ func (k Keeper) DeleteDiscontinueBucketsUntil(ctx sdk.Context, timestamp int64,
 
 			bucketDeleted, objectDeleted, err := k.ForceDeleteBucket(ctx, id, maxToDelete-deleted)
 			if err != nil {
-				ctx.Logger().Error("force delete bucket error", "err", err)
+				ctx.Logger().Error("force delete bucket error", "err", err, "id", id, "height", ctx.BlockHeight())
 				return deleted, err
 			}
 			deleted = deleted + objectDeleted
@@ -1693,6 +1738,205 @@ func (k Keeper) garbageCollectionForResource(ctx sdk.Context, deleteStalePolicie
 	return deletedTotal, true
 }
 
+func (k Keeper) MigrateBucket(ctx sdk.Context, operator sdk.AccAddress, bucketName string, dstPrimarySPID uint32, dstPrimarySPApproval *common.Approval, approvalBytes []byte) error {
+	store := ctx.KVStore(k.storeKey)
+
+	bucketInfo, found := k.GetBucketInfo(ctx, bucketName)
+	if !found {
+		return types.ErrNoSuchBucket
+	}
+
+	if !operator.Equals(sdk.MustAccAddressFromHex(bucketInfo.Owner)) {
+		return types.ErrAccessDenied.Wrap("Only bucket owner can migrate bucket.")
+	}
+
+	if bucketInfo.BucketStatus == types.BUCKET_STATUS_MIGRATING {
+		return types.ErrInvalidBucketStatus.Wrapf("The bucket already been migrating")
+	}
+
+	srcSP, found := k.spKeeper.GetStorageProvider(ctx, bucketInfo.PrimarySpId)
+	if !found {
+		return sptypes.ErrStorageProviderNotFound.Wrapf("src sp not found")
+	}
+
+	dstSP, found := k.spKeeper.GetStorageProvider(ctx, dstPrimarySPID)
+	if !found {
+		return sptypes.ErrStorageProviderNotFound.Wrapf("dst sp not found")
+	}
+
+	if !srcSP.IsInService() || !dstSP.IsInService() {
+		return sptypes.ErrStorageProviderNotInService.Wrapf(
+			"origin SP status: %s, dst SP status: %s", srcSP.Status.String(), dstSP.Status.String())
+	}
+
+	// check approval
+	if dstPrimarySPApproval.ExpiredHeight < (uint64)(ctx.BlockHeight()) {
+		return types.ErrInvalidApproval.Wrap("dst primary sp approval timeout")
+	}
+	err := k.VerifySPAndSignature(ctx, dstSP.Id, approvalBytes, dstPrimarySPApproval.Sig)
+	if err != nil {
+		return err
+	}
+
+	key := types.GetMigrationBucketKey(bucketInfo.Id)
+	if store.Has(key) {
+		panic("migration bucket key is existed.")
+	}
+
+	migrationBucketInfo := &types.MigrationBucketInfo{
+		SrcSpId:                       srcSP.Id,
+		DstSpId:                       dstSP.Id,
+		SrcGlobalVirtualGroupFamilyId: bucketInfo.GlobalVirtualGroupFamilyId,
+		BucketId:                      bucketInfo.Id,
+	}
+
+	bz := k.cdc.MustMarshal(migrationBucketInfo)
+	store.Set(key, bz)
+
+	bucketInfo.BucketStatus = types.BUCKET_STATUS_MIGRATING
+	k.SetBucketInfo(ctx, bucketInfo)
+
+	if err := ctx.EventManager().EmitTypedEvents(&types.EventMigrationBucket{
+		Operator:       operator.String(),
+		BucketName:     bucketName,
+		BucketId:       bucketInfo.Id,
+		DstPrimarySpId: dstSP.Id,
+	}); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (k Keeper) CompleteMigrateBucket(ctx sdk.Context, operator sdk.AccAddress, bucketName string, gvgFamilyID uint32, gvgMappings []*types.GVGMapping) error {
+	bucketInfo, found := k.GetBucketInfo(ctx, bucketName)
+	if !found {
+		return types.ErrNoSuchBucket
+	}
+
+	dstSP, found := k.spKeeper.GetStorageProviderByOperatorAddr(ctx, operator)
+	if !found {
+		return sptypes.ErrStorageProviderNotFound.Wrapf("dst SP not found.")
+	}
+
+	if bucketInfo.BucketStatus != types.BUCKET_STATUS_MIGRATING {
+		return types.ErrInvalidBucketStatus.Wrapf("The bucket is not been migrating")
+	}
+
+	migrationBucketInfo, found := k.GetMigrationBucketInfo(ctx, bucketInfo.Id)
+	if !found {
+		return types.ErrMigrationBucketFailed.Wrapf("migration bucket info not found.")
+	}
+
+	if dstSP.Id != migrationBucketInfo.DstSpId {
+		return types.ErrMigrationBucketFailed.Wrapf("dst sp info not match")
+	}
+
+	_, found = k.virtualGroupKeeper.GetGVGFamily(ctx, dstSP.Id, gvgFamilyID)
+	if !found {
+		return virtualgroupmoduletypes.ErrGVGFamilyNotExist
+	}
+
+	srcGvgFamily, found := k.virtualGroupKeeper.GetGVGFamily(ctx, bucketInfo.PrimarySpId, bucketInfo.GlobalVirtualGroupFamilyId)
+	if !found {
+		return virtualgroupmoduletypes.ErrGVGFamilyNotExist
+	}
+
+	sp, _ := k.spKeeper.GetStorageProvider(ctx, bucketInfo.PrimarySpId)
+
+	err := k.virtualGroupKeeper.SettleAndDistributeGVGFamily(ctx, sp, srcGvgFamily)
+	if err != nil {
+		return virtualgroupmoduletypes.ErrSettleFailed.Wrapf("settle gvg family failed, err: %s", err)
+	}
+
+	internalBucketInfo := k.MustGetInternalBucketInfo(ctx, bucketInfo.Id)
+	err = k.UnChargeBucketReadStoreFee(ctx, bucketInfo, internalBucketInfo)
+	if err != nil {
+		return types.ErrMigrationBucketFailed.Wrapf("cancel charge bucket failed, err: %s", err)
+	}
+
+	bucketInfo.PrimarySpId = migrationBucketInfo.DstSpId
+	bucketInfo.GlobalVirtualGroupFamilyId = gvgFamilyID
+
+	// check secondary sp signature
+	err = k.verifyGVGSignatures(ctx, bucketInfo.Id, dstSP, gvgMappings)
+	if err != nil {
+		return types.ErrMigrationBucketFailed.Wrapf("err: %s", err)
+	}
+
+	// rebinding gvg and lvg
+	err = k.RebindingVirtualGroup(ctx, bucketInfo, internalBucketInfo, gvgMappings)
+	if err != nil {
+		return types.ErrMigrationBucketFailed.Wrapf("err: %s", err)
+	}
+
+	bucketInfo.BucketStatus = types.BUCKET_STATUS_CREATED
+	k.SetBucketInfo(ctx, bucketInfo)
+	k.DeleteMigrationBucketInfo(ctx, bucketInfo.Id)
+
+	if err = ctx.EventManager().EmitTypedEvents(&types.EventCompleteMigrationBucket{
+		Operator:                   operator.String(),
+		BucketName:                 bucketName,
+		BucketId:                   bucketInfo.Id,
+		GlobalVirtualGroupFamilyId: gvgFamilyID,
+		GvgMappings:                gvgMappings,
+	}); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (k Keeper) CancelBucketMigration(ctx sdk.Context, operator sdk.AccAddress, bucketName string) error {
+	store := ctx.KVStore(k.storeKey)
+	bucketInfo, found := k.GetBucketInfo(ctx, bucketName)
+	if !found {
+		return types.ErrNoSuchBucket
+	}
+
+	if !operator.Equals(sdk.MustAccAddressFromHex(bucketInfo.Owner)) {
+		return types.ErrAccessDenied.Wrap("Only bucket owner can cancel migrate bucket.")
+	}
+
+	if bucketInfo.BucketStatus != types.BUCKET_STATUS_MIGRATING {
+		return types.ErrInvalidBucketStatus.Wrapf("The bucket is not been migrating")
+	}
+
+	key := types.GetMigrationBucketKey(bucketInfo.Id)
+	if !store.Has(key) {
+		return types.ErrMigrationBucketFailed.Wrapf("cancel migrate bucket failed due to the migrate bucket info not found.")
+	}
+
+	bucketInfo.BucketStatus = types.BUCKET_STATUS_CREATED
+	k.SetBucketInfo(ctx, bucketInfo)
+	store.Delete(key)
+
+	if err := ctx.EventManager().EmitTypedEvents(&types.EventCancelMigrationBucket{
+		Operator:   operator.String(),
+		BucketName: bucketName,
+		BucketId:   bucketInfo.Id,
+	}); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (k Keeper) GetMigrationBucketInfo(ctx sdk.Context, bucketID sdkmath.Uint) (*types.MigrationBucketInfo, bool) {
+	store := ctx.KVStore(k.storeKey)
+
+	bz := store.Get(types.GetMigrationBucketKey(bucketID))
+	if bz == nil {
+		return nil, false
+	}
+
+	var migrationBucketInfo types.MigrationBucketInfo
+	k.cdc.MustUnmarshal(bz, &migrationBucketInfo)
+	return &migrationBucketInfo, true
+}
+
+func (k Keeper) DeleteMigrationBucketInfo(ctx sdk.Context, bucketID sdkmath.Uint) {
+	store := ctx.KVStore(k.storeKey)
+	store.Delete(types.GetMigrationBucketKey(bucketID))
+}
+
 func (k Keeper) setQuotaUpdateTime(ctx sdk.Context, bucketId types.Uint, timestamp uint64) {
 	store := ctx.KVStore(k.storeKey)
 	bz := make([]byte, 8)
@@ -1712,3 +1956,31 @@ func (k Keeper) getQuotaUpdateTime(ctx sdk.Context, bucketId types.Uint) (uint64
 	}
 	return uint64(bucketInfo.CreateAt), true
 }
+
+func (k Keeper) MustGetInternalBucketInfo(ctx sdk.Context, bucketID sdkmath.Uint) *types.InternalBucketInfo {
+	internalBucketInfo, found := k.GetInternalBucketInfo(ctx, bucketID)
+	if !found {
+		panic("Internal bucket Info not found")
+	}
+	return internalBucketInfo
+}
+
+func (k Keeper) GetInternalBucketInfo(ctx sdk.Context, bucketID sdkmath.Uint) (*types.InternalBucketInfo, bool) {
+	store := ctx.KVStore(k.storeKey)
+
+	bz := store.Get(types.GetInternalBucketInfoKey(bucketID))
+	if bz == nil {
+		return nil, false
+	}
+
+	var internalBucketInfo types.InternalBucketInfo
+	k.cdc.MustUnmarshal(bz, &internalBucketInfo)
+	return &internalBucketInfo, true
+}
+
+func (k Keeper) SetInternalBucketInfo(ctx sdk.Context, bucketID sdkmath.Uint, internalBucketInfo *types.InternalBucketInfo) {
+	store := ctx.KVStore(k.storeKey)
+
+	bz := k.cdc.MustMarshal(internalBucketInfo)
+	store.Set(types.GetInternalBucketInfoKey(bucketID), bz)
+}
diff --git a/x/storage/keeper/msg_server.go b/x/storage/keeper/msg_server.go
index e89df2045..f2da636d3 100644
--- a/x/storage/keeper/msg_server.go
+++ b/x/storage/keeper/msg_server.go
@@ -3,17 +3,18 @@ package keeper
 import (
 	"context"
 
-	"cosmossdk.io/errors"
 	errorsmod "cosmossdk.io/errors"
-	"github.com/cosmos/cosmos-sdk/bsc/rlp"
+	"cosmossdk.io/math"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
 
 	types2 "github.com/bnb-chain/greenfield/types"
 	gnfderrors "github.com/bnb-chain/greenfield/types/errors"
 	permtypes "github.com/bnb-chain/greenfield/x/permission/types"
+	sptypes "github.com/bnb-chain/greenfield/x/sp/types"
 	"github.com/bnb-chain/greenfield/x/storage/types"
 	storagetypes "github.com/bnb-chain/greenfield/x/storage/types"
+	virtualgroupmoduletypes "github.com/bnb-chain/greenfield/x/virtualgroup/types"
 )
 
 type msgServer struct {
@@ -104,21 +105,20 @@ func (k msgServer) CreateObject(goCtx context.Context, msg *types.MsgCreateObjec
 
 	ownerAcc := sdk.MustAccAddressFromHex(msg.Creator)
 
-	if len(msg.ExpectChecksums) != int(1+k.GetExpectSecondarySPNumForECObject(ctx)) {
+	if len(msg.ExpectChecksums) != int(1+k.GetExpectSecondarySPNumForECObject(ctx, ctx.BlockTime().Unix())) {
 		return nil, gnfderrors.ErrInvalidChecksum.Wrapf("ExpectChecksums missing, expect: %d, actual: %d",
 			1+k.Keeper.RedundantParityChunkNum(ctx)+k.Keeper.RedundantParityChunkNum(ctx),
 			len(msg.ExpectChecksums))
 	}
 
 	id, err := k.Keeper.CreateObject(ctx, ownerAcc, msg.BucketName, msg.ObjectName, msg.PayloadSize, CreateObjectOptions{
-		SourceType:           types.SOURCE_TYPE_ORIGIN,
-		Visibility:           msg.Visibility,
-		ContentType:          msg.ContentType,
-		RedundancyType:       msg.RedundancyType,
-		Checksums:            msg.ExpectChecksums,
-		PrimarySpApproval:    msg.PrimarySpApproval,
-		ApprovalMsgBytes:     msg.GetApprovalBytes(),
-		SecondarySpAddresses: msg.ExpectSecondarySpAddresses,
+		SourceType:        types.SOURCE_TYPE_ORIGIN,
+		Visibility:        msg.Visibility,
+		ContentType:       msg.ContentType,
+		RedundancyType:    msg.RedundancyType,
+		Checksums:         msg.ExpectChecksums,
+		PrimarySpApproval: msg.PrimarySpApproval,
+		ApprovalMsgBytes:  msg.GetApprovalBytes(),
 	})
 	if err != nil {
 		return nil, err
@@ -147,20 +147,9 @@ func (k msgServer) SealObject(goCtx context.Context, msg *types.MsgSealObject) (
 
 	spSealAcc := sdk.MustAccAddressFromHex(msg.Operator)
 
-	expectSecondarySPNum := k.GetExpectSecondarySPNumForECObject(ctx)
-	if len(msg.SecondarySpAddresses) != (int)(expectSecondarySPNum) {
-		return nil, errors.Wrapf(gnfderrors.ErrInvalidSPAddress, "Missing SP expect (%d), but (%d)", expectSecondarySPNum,
-			len(msg.SecondarySpAddresses))
-	}
-
-	if len(msg.SecondarySpSignatures) != (int)(expectSecondarySPNum) {
-		return nil, errors.Wrapf(gnfderrors.ErrInvalidSPSignature, "Missing SP signatures, expect (%d), but (%d)",
-			expectSecondarySPNum, len(msg.SecondarySpSignatures))
-	}
-
 	err := k.Keeper.SealObject(ctx, spSealAcc, msg.BucketName, msg.ObjectName, SealObjectOptions{
-		SecondarySpAddresses:  msg.SecondarySpAddresses,
-		SecondarySpSignatures: msg.SecondarySpSignatures,
+		GlobalVirtualGroupId:     msg.GlobalVirtualGroupId,
+		SecondarySpBlsSignatures: msg.SecondarySpBlsAggSignatures,
 	})
 
 	if err != nil {
@@ -411,7 +400,7 @@ func (k msgServer) MirrorObject(goCtx context.Context, msg *types.MsgMirrorObjec
 		Owner: owner,
 	}
 
-	encodedPackage, err := rlp.EncodeToBytes(mirrorPackage)
+	encodedPackage, err := mirrorPackage.Serialize()
 	if err != nil {
 		return nil, types.ErrInvalidCrossChainPackage
 	}
@@ -420,16 +409,13 @@ func (k msgServer) MirrorObject(goCtx context.Context, msg *types.MsgMirrorObjec
 		OperationType: types.OperationMirrorObject,
 		Package:       encodedPackage,
 	}
-	encodedWrapPackage, err := rlp.EncodeToBytes(wrapPackage)
-	if err != nil {
-		return nil, types.ErrInvalidCrossChainPackage
-	}
+	encodedWrapPackage := wrapPackage.MustSerialize()
 
 	relayerFee := k.Keeper.MirrorObjectRelayerFee(ctx)
 	ackRelayerFee := k.Keeper.MirrorObjectAckRelayerFee(ctx)
 
-	_, err = k.crossChainKeeper.CreateRawIBCPackageWithFee(ctx, types.ObjectChannelId, sdk.SynCrossChainPackageType,
-		encodedWrapPackage, relayerFee, ackRelayerFee)
+	_, err = k.crossChainKeeper.CreateRawIBCPackageWithFee(ctx, k.crossChainKeeper.GetDestBscChainID(),
+		types.ObjectChannelId, sdk.SynCrossChainPackageType, encodedWrapPackage, relayerFee, ackRelayerFee)
 	if err != nil {
 		return nil, err
 	}
@@ -439,10 +425,11 @@ func (k msgServer) MirrorObject(goCtx context.Context, msg *types.MsgMirrorObjec
 	k.Keeper.SetObjectInfo(ctx, objectInfo)
 
 	if err := ctx.EventManager().EmitTypedEvents(&types.EventMirrorObject{
-		Operator:   objectInfo.Owner,
-		BucketName: objectInfo.BucketName,
-		ObjectName: objectInfo.ObjectName,
-		ObjectId:   objectInfo.Id,
+		Operator:    objectInfo.Owner,
+		BucketName:  objectInfo.BucketName,
+		ObjectName:  objectInfo.ObjectName,
+		ObjectId:    objectInfo.Id,
+		DestChainId: uint32(k.crossChainKeeper.GetDestBscChainID()),
 	}); err != nil {
 		return nil, err
 	}
@@ -483,7 +470,7 @@ func (k msgServer) MirrorBucket(goCtx context.Context, msg *types.MsgMirrorBucke
 		Owner: owner,
 	}
 
-	encodedPackage, err := rlp.EncodeToBytes(mirrorPackage)
+	encodedPackage, err := mirrorPackage.Serialize()
 	if err != nil {
 		return nil, types.ErrInvalidCrossChainPackage
 	}
@@ -492,16 +479,13 @@ func (k msgServer) MirrorBucket(goCtx context.Context, msg *types.MsgMirrorBucke
 		OperationType: types.OperationMirrorBucket,
 		Package:       encodedPackage,
 	}
-	encodedWrapPackage, err := rlp.EncodeToBytes(wrapPackage)
-	if err != nil {
-		return nil, types.ErrInvalidCrossChainPackage
-	}
+	encodedWrapPackage := wrapPackage.MustSerialize()
 
 	relayerFee := k.Keeper.MirrorBucketRelayerFee(ctx)
 	ackRelayerFee := k.Keeper.MirrorBucketAckRelayerFee(ctx)
 
-	_, err = k.crossChainKeeper.CreateRawIBCPackageWithFee(ctx, types.BucketChannelId, sdk.SynCrossChainPackageType,
-		encodedWrapPackage, relayerFee, ackRelayerFee)
+	_, err = k.crossChainKeeper.CreateRawIBCPackageWithFee(ctx, k.crossChainKeeper.GetDestBscChainID(),
+		types.BucketChannelId, sdk.SynCrossChainPackageType, encodedWrapPackage, relayerFee, ackRelayerFee)
 	if err != nil {
 		return nil, err
 	}
@@ -511,9 +495,10 @@ func (k msgServer) MirrorBucket(goCtx context.Context, msg *types.MsgMirrorBucke
 	k.Keeper.SetBucketInfo(ctx, bucketInfo)
 
 	if err := ctx.EventManager().EmitTypedEvents(&types.EventMirrorBucket{
-		Operator:   bucketInfo.Owner,
-		BucketName: bucketInfo.BucketName,
-		BucketId:   bucketInfo.Id,
+		Operator:    bucketInfo.Owner,
+		BucketName:  bucketInfo.BucketName,
+		BucketId:    bucketInfo.Id,
+		DestChainId: uint32(k.crossChainKeeper.GetDestBscChainID()),
 	}); err != nil {
 		return nil, err
 	}
@@ -550,7 +535,7 @@ func (k msgServer) MirrorGroup(goCtx context.Context, msg *types.MsgMirrorGroup)
 		Owner: operator,
 	}
 
-	encodedPackage, err := rlp.EncodeToBytes(mirrorPackage)
+	encodedPackage, err := mirrorPackage.Serialize()
 	if err != nil {
 		return nil, types.ErrInvalidCrossChainPackage
 	}
@@ -559,16 +544,13 @@ func (k msgServer) MirrorGroup(goCtx context.Context, msg *types.MsgMirrorGroup)
 		OperationType: types.OperationMirrorGroup,
 		Package:       encodedPackage,
 	}
-	encodedWrapPackage, err := rlp.EncodeToBytes(wrapPackage)
-	if err != nil {
-		return nil, types.ErrInvalidCrossChainPackage
-	}
+	encodedWrapPackage := wrapPackage.MustSerialize()
 
 	relayerFee := k.Keeper.MirrorGroupRelayerFee(ctx)
 	ackRelayerFee := k.Keeper.MirrorGroupAckRelayerFee(ctx)
 
-	_, err = k.crossChainKeeper.CreateRawIBCPackageWithFee(ctx, types.GroupChannelId, sdk.SynCrossChainPackageType,
-		encodedWrapPackage, relayerFee, ackRelayerFee)
+	_, err = k.crossChainKeeper.CreateRawIBCPackageWithFee(ctx, k.crossChainKeeper.GetDestBscChainID(),
+		types.GroupChannelId, sdk.SynCrossChainPackageType, encodedWrapPackage, relayerFee, ackRelayerFee)
 	if err != nil {
 		return nil, err
 	}
@@ -578,9 +560,10 @@ func (k msgServer) MirrorGroup(goCtx context.Context, msg *types.MsgMirrorGroup)
 	k.Keeper.SetGroupInfo(ctx, groupInfo)
 
 	if err := ctx.EventManager().EmitTypedEvents(&types.EventMirrorGroup{
-		Owner:     groupInfo.Owner,
-		GroupName: groupInfo.GroupName,
-		GroupId:   groupInfo.Id,
+		Owner:       groupInfo.Owner,
+		GroupName:   groupInfo.GroupName,
+		GroupId:     groupInfo.Id,
+		DestChainId: uint32(k.crossChainKeeper.GetDestBscChainID()),
 	}); err != nil {
 		return nil, err
 	}
@@ -600,3 +583,58 @@ func (k msgServer) UpdateParams(goCtx context.Context, req *types.MsgUpdateParam
 
 	return &types.MsgUpdateParamsResponse{}, nil
 }
+
+func (k msgServer) MigrateBucket(goCtx context.Context, msg *types.MsgMigrateBucket) (*types.MsgMigrateBucketResponse, error) {
+	ctx := sdk.UnwrapSDKContext(goCtx)
+	operator := sdk.MustAccAddressFromHex(msg.Operator)
+
+	err := k.Keeper.MigrateBucket(ctx, operator, msg.BucketName, msg.DstPrimarySpId, msg.DstPrimarySpApproval, msg.GetApprovalBytes())
+	if err != nil {
+		return nil, err
+	}
+
+	return &types.MsgMigrateBucketResponse{}, nil
+}
+
+func (k msgServer) CompleteMigrateBucket(goCtx context.Context, msg *types.MsgCompleteMigrateBucket) (*types.MsgCompleteMigrateBucketResponse, error) {
+	ctx := sdk.UnwrapSDKContext(goCtx)
+
+	operator := sdk.MustAccAddressFromHex(msg.Operator)
+
+	err := k.Keeper.CompleteMigrateBucket(ctx, operator, msg.BucketName, msg.GlobalVirtualGroupFamilyId, msg.GvgMappings)
+	if err != nil {
+		return nil, err
+	}
+
+	return &types.MsgCompleteMigrateBucketResponse{}, nil
+}
+
+func (k msgServer) CancelMigrateBucket(goCtx context.Context, msg *types.MsgCancelMigrateBucket) (*types.MsgCancelMigrateBucketResponse, error) {
+	ctx := sdk.UnwrapSDKContext(goCtx)
+
+	operator := sdk.MustAccAddressFromHex(msg.Operator)
+
+	err := k.CancelBucketMigration(ctx, operator, msg.BucketName)
+	if err != nil {
+		return nil, err
+	}
+
+	return &types.MsgCancelMigrateBucketResponse{}, nil
+}
+
+func (k Keeper) verifyGVGSignatures(ctx sdk.Context, bucketID math.Uint, dstSP *sptypes.StorageProvider, gvgMappings []*storagetypes.GVGMapping) error {
+	// verify secondary sp signature
+	for _, newLvg2gvg := range gvgMappings {
+		dstGVG, found := k.virtualGroupKeeper.GetGVG(ctx, newLvg2gvg.DstGlobalVirtualGroupId)
+		if !found {
+			return virtualgroupmoduletypes.ErrGVGNotExist.Wrapf("dst gvg not found")
+		}
+		// validate the aggregated bls signature
+		migrationBucketSignDocBlsSignHash := storagetypes.NewSecondarySpMigrationBucketSignDoc(ctx.ChainID(), bucketID, dstSP.Id, newLvg2gvg.SrcGlobalVirtualGroupId, dstGVG.Id).GetBlsSignHash()
+		err := k.VerifyGVGSecondarySPsBlsSignature(ctx, dstGVG, migrationBucketSignDocBlsSignHash, newLvg2gvg.SecondarySpBlsSignature)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
diff --git a/x/storage/keeper/options.go b/x/storage/keeper/options.go
index d16052bc8..5cf66a102 100644
--- a/x/storage/keeper/options.go
+++ b/x/storage/keeper/options.go
@@ -1,6 +1,7 @@
 package keeper
 
 import (
+	"github.com/bnb-chain/greenfield/types/common"
 	"github.com/bnb-chain/greenfield/x/storage/types"
 )
 
@@ -9,7 +10,7 @@ type CreateBucketOptions struct {
 	SourceType        types.SourceType
 	ChargedReadQuota  uint64
 	PaymentAddress    string
-	PrimarySpApproval *types.Approval
+	PrimarySpApproval *common.Approval
 	ApprovalMsgBytes  []byte
 }
 
@@ -25,14 +26,13 @@ type UpdateBucketOptions struct {
 }
 
 type CreateObjectOptions struct {
-	Visibility           types.VisibilityType
-	ContentType          string
-	SourceType           types.SourceType
-	RedundancyType       types.RedundancyType
-	Checksums            [][]byte
-	SecondarySpAddresses []string
-	PrimarySpApproval    *types.Approval
-	ApprovalMsgBytes     []byte
+	Visibility        types.VisibilityType
+	ContentType       string
+	SourceType        types.SourceType
+	RedundancyType    types.RedundancyType
+	Checksums         [][]byte
+	PrimarySpApproval *common.Approval
+	ApprovalMsgBytes  []byte
 }
 
 type CancelCreateObjectOptions struct {
@@ -46,7 +46,7 @@ type DeleteObjectOptions struct {
 type CopyObjectOptions struct {
 	SourceType        types.SourceType
 	Visibility        types.VisibilityType
-	PrimarySpApproval *types.Approval
+	PrimarySpApproval *common.Approval
 	ApprovalMsgBytes  []byte
 }
 type CreateGroupOptions struct {
diff --git a/x/storage/keeper/params.go b/x/storage/keeper/params.go
index 74f3ae9f0..f1ae5b3a1 100644
--- a/x/storage/keeper/params.go
+++ b/x/storage/keeper/params.go
@@ -15,8 +15,12 @@ func (k Keeper) MaxBucketsPerAccount(ctx sdk.Context) (res uint32) {
 	return params.MaxBucketsPerAccount
 }
 
-func (k Keeper) GetExpectSecondarySPNumForECObject(ctx sdk.Context) (res uint32) {
-	return k.RedundantDataChunkNum(ctx) + k.RedundantParityChunkNum(ctx)
+func (k Keeper) GetExpectSecondarySPNumForECObject(ctx sdk.Context, createTime int64) (res uint32) {
+	versionParams, err := k.GetVersionedParamsWithTs(ctx, createTime)
+	if err != nil {
+		panic(fmt.Sprintf("get expect secondary sp num error, msg: %s", err))
+	}
+	return versionParams.RedundantParityChunkNum + versionParams.RedundantDataChunkNum
 }
 
 func (k Keeper) MaxPayloadSize(ctx sdk.Context) (res uint64) {
@@ -26,7 +30,7 @@ func (k Keeper) MaxPayloadSize(ctx sdk.Context) (res uint64) {
 
 func (k Keeper) MirrorBucketRelayerFee(ctx sdk.Context) *big.Int {
 	params := k.GetParams(ctx)
-	relayerFeeParam := params.MirrorBucketRelayerFee
+	relayerFeeParam := params.BscMirrorBucketRelayerFee
 	relayerFee, valid := big.NewInt(0).SetString(relayerFeeParam, 10)
 	if !valid {
 		panic(fmt.Sprintf("invalid relayer fee: %s", relayerFeeParam))
@@ -37,7 +41,7 @@ func (k Keeper) MirrorBucketRelayerFee(ctx sdk.Context) *big.Int {
 
 func (k Keeper) MirrorBucketAckRelayerFee(ctx sdk.Context) *big.Int {
 	params := k.GetParams(ctx)
-	relayerFeeParam := params.MirrorBucketAckRelayerFee
+	relayerFeeParam := params.BscMirrorBucketAckRelayerFee
 
 	relayerFee, valid := big.NewInt(0).SetString(relayerFeeParam, 10)
 	if !valid {
@@ -49,7 +53,7 @@ func (k Keeper) MirrorBucketAckRelayerFee(ctx sdk.Context) *big.Int {
 
 func (k Keeper) MirrorObjectRelayerFee(ctx sdk.Context) *big.Int {
 	params := k.GetParams(ctx)
-	relayerFeeParam := params.MirrorObjectRelayerFee
+	relayerFeeParam := params.BscMirrorObjectRelayerFee
 
 	relayerFee, valid := big.NewInt(0).SetString(relayerFeeParam, 10)
 	if !valid {
@@ -61,7 +65,7 @@ func (k Keeper) MirrorObjectRelayerFee(ctx sdk.Context) *big.Int {
 
 func (k Keeper) MirrorObjectAckRelayerFee(ctx sdk.Context) *big.Int {
 	params := k.GetParams(ctx)
-	relayerFeeParam := params.MirrorObjectAckRelayerFee
+	relayerFeeParam := params.BscMirrorObjectAckRelayerFee
 
 	relayerFee, valid := big.NewInt(0).SetString(relayerFeeParam, 10)
 	if !valid {
@@ -73,7 +77,7 @@ func (k Keeper) MirrorObjectAckRelayerFee(ctx sdk.Context) *big.Int {
 
 func (k Keeper) MirrorGroupRelayerFee(ctx sdk.Context) *big.Int {
 	params := k.GetParams(ctx)
-	relayerFeeParam := params.MirrorGroupRelayerFee
+	relayerFeeParam := params.BscMirrorGroupRelayerFee
 
 	relayerFee, valid := big.NewInt(0).SetString(relayerFeeParam, 10)
 	if !valid {
@@ -85,7 +89,7 @@ func (k Keeper) MirrorGroupRelayerFee(ctx sdk.Context) *big.Int {
 
 func (k Keeper) MirrorGroupAckRelayerFee(ctx sdk.Context) *big.Int {
 	params := k.GetParams(ctx)
-	relayerFeeParam := params.MirrorGroupAckRelayerFee
+	relayerFeeParam := params.BscMirrorGroupAckRelayerFee
 
 	relayerFee, valid := big.NewInt(0).SetString(relayerFeeParam, 10)
 	if !valid {
@@ -207,3 +211,8 @@ func (k Keeper) GetVersionedParamsWithTs(ctx sdk.Context, ts int64) (verParams t
 
 	return verParams, nil
 }
+
+func (k Keeper) MaxLocalVirtualGroupNumPerBucket(ctx sdk.Context) (res uint32) {
+	params := k.GetParams(ctx)
+	return params.MaxLocalVirtualGroupNumPerBucket
+}
diff --git a/x/storage/keeper/payment.go b/x/storage/keeper/payment.go
index 75e8a7c8d..637e62fcf 100644
--- a/x/storage/keeper/payment.go
+++ b/x/storage/keeper/payment.go
@@ -2,7 +2,6 @@ package keeper
 
 import (
 	"fmt"
-	"sort"
 
 	"cosmossdk.io/errors"
 	sdkmath "cosmossdk.io/math"
@@ -12,48 +11,96 @@ import (
 	storagetypes "github.com/bnb-chain/greenfield/x/storage/types"
 )
 
-func (k Keeper) GetFundingAddressBySpAddr(ctx sdk.Context, spAddr sdk.AccAddress) (string, error) {
-	sp, found := k.spKeeper.GetStorageProvider(ctx, spAddr)
-	if !found {
-		return "", fmt.Errorf("storage provider %s not found", spAddr)
-	}
-	return sp.FundingAddress, nil
-}
-
-func (k Keeper) ChargeInitialReadFee(ctx sdk.Context, bucketInfo *storagetypes.BucketInfo) error {
+func (k Keeper) ChargeBucketReadFee(ctx sdk.Context, bucketInfo *storagetypes.BucketInfo,
+	internalBucketInfo *storagetypes.InternalBucketInfo) error {
 	if bucketInfo.ChargedReadQuota == 0 {
 		return nil
 	}
-	bucketInfo.BillingInfo.PriceTime = ctx.BlockTime().Unix()
-	bill, err := k.GetBucketBill(ctx, bucketInfo)
+	internalBucketInfo.PriceTime = ctx.BlockTime().Unix()
+	bill, err := k.GetBucketReadBill(ctx, bucketInfo, internalBucketInfo)
 	if err != nil {
-		return fmt.Errorf("get bucket bill failed: %w", err)
+		return fmt.Errorf("charge bucket read fee failed, get bucket bill failed, bucket: %s, err: %s", bucketInfo.BucketName, err.Error())
+	}
+	err = k.paymentKeeper.ApplyUserFlowsList(ctx, []types.UserFlows{bill})
+	if err != nil {
+		ctx.Logger().Error("charge initial read fee failed", "err", err.Error())
+		return err
 	}
-	return k.paymentKeeper.ApplyUserFlowsList(ctx, []types.UserFlows{bill})
+	return nil
 }
 
-func (k Keeper) ChargeDeleteBucket(ctx sdk.Context, bucketInfo *storagetypes.BucketInfo) error {
-	bill, err := k.GetBucketBill(ctx, bucketInfo)
+func (k Keeper) UnChargeBucketReadFee(ctx sdk.Context, bucketInfo *storagetypes.BucketInfo,
+	internalBucketInfo *storagetypes.InternalBucketInfo) error {
+	if internalBucketInfo.TotalChargeSize > 0 {
+		return fmt.Errorf("unexpected total store charge size: %d", internalBucketInfo.TotalChargeSize)
+	}
+
+	bill, err := k.GetBucketReadBill(ctx, bucketInfo, internalBucketInfo)
 	if err != nil {
-		return err
+		return fmt.Errorf("uncharge bucket read fee failed, get bucket bill failed, bucket: %s, err: %s", bucketInfo.BucketName, err.Error())
 	}
 	if len(bill.Flows) == 0 {
 		return nil
 	}
-	// should only remain at most 2 flows: charged_read_quota fee and tax
-	if len(bill.Flows) > 2 {
-		panic(fmt.Sprintf("unexpected left flow number: %d", len(bill.Flows)))
-	}
-	bill.Flows = GetNegFlows(bill.Flows)
+	bill.Flows = getNegFlows(bill.Flows)
 	err = k.paymentKeeper.ApplyUserFlowsList(ctx, []types.UserFlows{bill})
-	return err
+	if err != nil {
+		ctx.Logger().Error("uncharge bucket read fee failed", "err", err.Error())
+		return err
+	}
+	return nil
 }
 
-func (k Keeper) UpdateBucketInfoAndCharge(ctx sdk.Context, bucketInfo *storagetypes.BucketInfo, newPaymentAddr string, newReadQuota uint64) error {
+func (k Keeper) GetBucketReadBill(ctx sdk.Context, bucketInfo *storagetypes.BucketInfo,
+	internalBucketInfo *storagetypes.InternalBucketInfo) (userFlows types.UserFlows, err error) {
+	userFlows.From = sdk.MustAccAddressFromHex(bucketInfo.PaymentAddress)
+	if bucketInfo.ChargedReadQuota == 0 {
+		return userFlows, nil
+	}
+	price, err := k.paymentKeeper.GetStoragePrice(ctx, types.StoragePriceParams{
+		PrimarySp: bucketInfo.PrimarySpId,
+		PriceTime: internalBucketInfo.PriceTime,
+	})
+	if err != nil {
+		return userFlows, fmt.Errorf("get storage price failed: %w", err)
+	}
+
+	gvgFamily, found := k.virtualGroupKeeper.GetGVGFamily(ctx, bucketInfo.PrimarySpId, bucketInfo.GlobalVirtualGroupFamilyId)
+	if !found {
+		return userFlows, fmt.Errorf("get GVG family failed: %d", bucketInfo.GlobalVirtualGroupFamilyId)
+	}
+
+	// primary sp total rate
+	primaryTotalFlowRate := price.ReadPrice.MulInt(sdkmath.NewIntFromUint64(bucketInfo.ChargedReadQuota)).TruncateInt()
+
+	if primaryTotalFlowRate.IsPositive() {
+		userFlows.Flows = append(userFlows.Flows, types.OutFlow{
+			ToAddress: gvgFamily.VirtualPaymentAddress,
+			Rate:      primaryTotalFlowRate,
+		})
+	}
+
+	versionedParams, err := k.paymentKeeper.GetVersionedParamsWithTs(ctx, internalBucketInfo.PriceTime)
+	if err != nil {
+		return userFlows, fmt.Errorf("failed to get validator tax rate: %w, time: %d", err, internalBucketInfo.PriceTime)
+	}
+	validatorTaxRate := versionedParams.ValidatorTaxRate.MulInt(primaryTotalFlowRate).TruncateInt()
+	if validatorTaxRate.IsPositive() {
+		userFlows.Flows = append(userFlows.Flows, types.OutFlow{
+			ToAddress: types.ValidatorTaxPoolAddress.String(),
+			Rate:      validatorTaxRate,
+		})
+	}
+
+	return userFlows, nil
+}
+
+func (k Keeper) UpdateBucketInfoAndCharge(ctx sdk.Context, bucketInfo *storagetypes.BucketInfo,
+	internalBucketInfo *storagetypes.InternalBucketInfo, newPaymentAddr string, newReadQuota uint64) error {
 	if bucketInfo.PaymentAddress != newPaymentAddr && bucketInfo.ChargedReadQuota != newReadQuota {
 		return fmt.Errorf("payment address and read quota can not be changed at the same time")
 	}
-	err := k.ChargeViaBucketChange(ctx, bucketInfo, func(bi *storagetypes.BucketInfo) error {
+	err := k.ChargeViaBucketChange(ctx, bucketInfo, internalBucketInfo, func(bi *storagetypes.BucketInfo, ibi *storagetypes.InternalBucketInfo) error {
 		bi.PaymentAddress = newPaymentAddr
 		bi.ChargedReadQuota = newReadQuota
 		return nil
@@ -61,9 +108,12 @@ func (k Keeper) UpdateBucketInfoAndCharge(ctx sdk.Context, bucketInfo *storagety
 	return err
 }
 
-func (k Keeper) LockStoreFee(ctx sdk.Context, bucketInfo *storagetypes.BucketInfo, objectInfo *storagetypes.ObjectInfo) error {
+func (k Keeper) LockObjectStoreFee(ctx sdk.Context, bucketInfo *storagetypes.BucketInfo, objectInfo *storagetypes.ObjectInfo) error {
 	paymentAddr := sdk.MustAccAddressFromHex(bucketInfo.PaymentAddress)
-	amount, err := k.GetObjectLockFee(ctx, bucketInfo.PrimarySpAddress, objectInfo.CreateAt, objectInfo.PayloadSize)
+	amount, err := k.GetObjectLockFee(ctx, bucketInfo.PrimarySpId, objectInfo.CreateAt, objectInfo.PayloadSize)
+	if err != nil {
+		return fmt.Errorf("get object store fee rate failed: %w", err)
+	}
 	if ctx.IsCheckTx() {
 		_ = ctx.EventManager().EmitTypedEvents(&types.EventFeePreview{
 			Account:        paymentAddr.String(),
@@ -71,9 +121,7 @@ func (k Keeper) LockStoreFee(ctx sdk.Context, bucketInfo *storagetypes.BucketInf
 			Amount:         amount,
 		})
 	}
-	if err != nil {
-		return fmt.Errorf("get object store fee rate failed: %w", err)
-	}
+
 	change := types.NewDefaultStreamRecordChangeWithAddr(paymentAddr).WithLockBalanceChange(amount)
 	streamRecord, err := k.paymentKeeper.UpdateStreamRecordByAddr(ctx, change)
 	if err != nil {
@@ -85,9 +133,9 @@ func (k Keeper) LockStoreFee(ctx sdk.Context, bucketInfo *storagetypes.BucketInf
 	return nil
 }
 
-// UnlockStoreFee unlock store fee if the object is deleted in INIT state
-func (k Keeper) UnlockStoreFee(ctx sdk.Context, bucketInfo *storagetypes.BucketInfo, objectInfo *storagetypes.ObjectInfo) error {
-	lockedBalance, err := k.GetObjectLockFee(ctx, bucketInfo.PrimarySpAddress, objectInfo.CreateAt, objectInfo.PayloadSize)
+// UnlockObjectStoreFee unlock store fee if the object is deleted in INIT state
+func (k Keeper) UnlockObjectStoreFee(ctx sdk.Context, bucketInfo *storagetypes.BucketInfo, objectInfo *storagetypes.ObjectInfo) error {
+	lockedBalance, err := k.GetObjectLockFee(ctx, bucketInfo.PrimarySpId, objectInfo.CreateAt, objectInfo.PayloadSize)
 	if err != nil {
 		return fmt.Errorf("get object store fee rate failed: %w", err)
 	}
@@ -100,218 +148,451 @@ func (k Keeper) UnlockStoreFee(ctx sdk.Context, bucketInfo *storagetypes.BucketI
 	return nil
 }
 
-func (k Keeper) UnlockAndChargeStoreFee(ctx sdk.Context, bucketInfo *storagetypes.BucketInfo, objectInfo *storagetypes.ObjectInfo) error {
+func (k Keeper) UnlockAndChargeObjectStoreFee(ctx sdk.Context, bucketInfo *storagetypes.BucketInfo,
+	internalBucketInfo *storagetypes.InternalBucketInfo, objectInfo *storagetypes.ObjectInfo) error {
 	// unlock store fee
-	err := k.UnlockStoreFee(ctx, bucketInfo, objectInfo)
+	err := k.UnlockObjectStoreFee(ctx, bucketInfo, objectInfo)
 	if err != nil {
 		return fmt.Errorf("unlock store fee failed: %w", err)
 	}
 
-	return k.ChargeStoreFee(ctx, bucketInfo, objectInfo)
+	return k.ChargeObjectStoreFee(ctx, bucketInfo, internalBucketInfo, objectInfo)
 }
 
-func (k Keeper) ChargeStoreFee(ctx sdk.Context, bucketInfo *storagetypes.BucketInfo, objectInfo *storagetypes.ObjectInfo) error {
-	chargeSize, err := k.GetChargeSize(ctx, objectInfo.PayloadSize, objectInfo.CreateAt)
+func (k Keeper) IsPriceChanged(ctx sdk.Context, primarySpId uint32, priceTime int64) (bool, error) {
+	prePrice, err := k.paymentKeeper.GetStoragePrice(ctx, types.StoragePriceParams{
+		PrimarySp: primarySpId,
+		PriceTime: priceTime,
+	})
+	if err != nil {
+		return false, fmt.Errorf("get storage price failed: %w", err)
+	}
+	currentPrice, err := k.paymentKeeper.GetStoragePrice(ctx, types.StoragePriceParams{
+		PrimarySp: primarySpId,
+		PriceTime: ctx.BlockTime().Unix(),
+	})
+	if err != nil {
+		return false, fmt.Errorf("get storage price failed: %w", err)
+	}
+
+	return !(prePrice.ReadPrice.Equal(currentPrice.ReadPrice) &&
+		prePrice.PrimaryStorePrice.Equal(currentPrice.PrimaryStorePrice) &&
+		prePrice.SecondaryStorePrice.Equal(currentPrice.SecondaryStorePrice)), nil
+}
+
+func (k Keeper) ChargeObjectStoreFee(ctx sdk.Context, bucketInfo *storagetypes.BucketInfo,
+	internalBucketInfo *storagetypes.InternalBucketInfo, objectInfo *storagetypes.ObjectInfo) error {
+	chargeSize, err := k.GetObjectChargeSize(ctx, objectInfo.PayloadSize, objectInfo.CreateAt)
 	if err != nil {
 		return fmt.Errorf("get charge size error: %w", err)
 	}
-	return k.ChargeViaBucketChange(ctx, bucketInfo, func(bi *storagetypes.BucketInfo) error {
-		bi.BillingInfo.TotalChargeSize += chargeSize
-		secondarySpObjectsSize := bi.BillingInfo.SecondarySpObjectsSize
-		for _, sp := range objectInfo.SecondarySpAddresses {
-			secondarySpObjectsSize = append(secondarySpObjectsSize, storagetypes.SecondarySpObjectsSize{
-				SpAddress:       sp,
-				TotalChargeSize: chargeSize,
-			})
+
+	priceChanged, err := k.IsPriceChanged(ctx, bucketInfo.PrimarySpId, internalBucketInfo.PriceTime)
+	if err != nil {
+		return fmt.Errorf("check whether price changed error: %w", err)
+	}
+
+	if !priceChanged {
+		err := k.ChargeViaObjectChange(ctx, bucketInfo, internalBucketInfo, objectInfo, chargeSize, false)
+		if err != nil {
+			return fmt.Errorf("apply object store bill error: %w", err)
 		}
-		bi.BillingInfo.SecondarySpObjectsSize = MergeSecondarySpObjectsSize(secondarySpObjectsSize)
 		return nil
+	}
+
+	return k.ChargeViaBucketChange(ctx, bucketInfo, internalBucketInfo, func(bi *storagetypes.BucketInfo, ibi *storagetypes.InternalBucketInfo) error {
+		ibi.TotalChargeSize += chargeSize
+		for _, lvg := range ibi.LocalVirtualGroups {
+			if lvg.Id == objectInfo.LocalVirtualGroupId {
+				lvg.TotalChargeSize += lvg.TotalChargeSize + chargeSize
+				break
+			}
+		}
+		return nil
+	})
+}
+
+func (k Keeper) UnChargeObjectStoreFee(ctx sdk.Context, bucketInfo *storagetypes.BucketInfo,
+	internalBucketInfo *storagetypes.InternalBucketInfo, objectInfo *storagetypes.ObjectInfo) error {
+	chargeSize, err := k.GetObjectChargeSize(ctx, objectInfo.PayloadSize, objectInfo.CreateAt)
+	if err != nil {
+		return fmt.Errorf("get charge size error: %w", err)
+	}
+
+	priceChanged, err := k.IsPriceChanged(ctx, bucketInfo.PrimarySpId, internalBucketInfo.PriceTime)
+	if err != nil {
+		return fmt.Errorf("check whether price changed error: %w", err)
+	}
+
+	oldInternalBucketInfo := &storagetypes.InternalBucketInfo{
+		PriceTime:          internalBucketInfo.PriceTime,
+		TotalChargeSize:    internalBucketInfo.TotalChargeSize,
+		LocalVirtualGroups: internalBucketInfo.LocalVirtualGroups,
+	}
+
+	if !priceChanged {
+		err = k.ChargeViaObjectChange(ctx, bucketInfo, internalBucketInfo, objectInfo, chargeSize, true)
+		if err != nil {
+			return fmt.Errorf("apply object store bill error: %w", err)
+		}
+	} else {
+		err = k.ChargeViaBucketChange(ctx, bucketInfo, internalBucketInfo, func(bi *storagetypes.BucketInfo, ibi *storagetypes.InternalBucketInfo) error {
+			ibi.TotalChargeSize -= chargeSize
+			for _, lvg := range ibi.LocalVirtualGroups {
+				if lvg.Id == objectInfo.LocalVirtualGroupId {
+					lvg.TotalChargeSize -= chargeSize
+					break
+				}
+			}
+			return nil
+		})
+		if err != nil {
+			return fmt.Errorf("apply object store bill error: %w", err)
+		}
+	}
+
+	blockTime := ctx.BlockTime().Unix()
+	versionParams, err := k.paymentKeeper.GetVersionedParamsWithTs(ctx, oldInternalBucketInfo.PriceTime)
+	if err != nil {
+		return fmt.Errorf("failed to get versioned params: %w", err)
+	}
+	timeToPay := objectInfo.CreateAt + int64(versionParams.ReserveTime) - blockTime
+	if timeToPay > 0 { // store less than reserve time
+		err = k.ChargeObjectStoreFeeForEarlyDeletion(ctx, bucketInfo, oldInternalBucketInfo, objectInfo, chargeSize, timeToPay)
+		forced, _ := ctx.Value(types.ForceUpdateStreamRecordKey).(bool) // force update in end block
+		if !forced && err != nil {
+			return fmt.Errorf("fail to pay for early deletion, error: %w", err)
+		}
+	}
+	return nil
+}
+
+func (k Keeper) ChargeObjectStoreFeeForEarlyDeletion(ctx sdk.Context, bucketInfo *storagetypes.BucketInfo,
+	internalBucketInfo *storagetypes.InternalBucketInfo, objectInfo *storagetypes.ObjectInfo,
+	chargeSize uint64, timeToPay int64) error {
+	paymentAddr := sdk.MustAccAddressFromHex(bucketInfo.PaymentAddress)
+	price, err := k.paymentKeeper.GetStoragePrice(ctx, types.StoragePriceParams{
+		PrimarySp: bucketInfo.PrimarySpId,
+		PriceTime: internalBucketInfo.PriceTime,
 	})
+	if err != nil {
+		return fmt.Errorf("get storage price failed: %w", err)
+	}
+
+	// primary sp total rate
+	primaryTotalFlowRate := sdk.ZeroInt()
+
+	gvgFamily, found := k.virtualGroupKeeper.GetGVGFamily(ctx, bucketInfo.PrimarySpId, bucketInfo.GlobalVirtualGroupFamilyId)
+	if !found {
+		return fmt.Errorf("get GVG family failed: %d", bucketInfo.GlobalVirtualGroupFamilyId)
+	}
+
+	primaryRate := price.PrimaryStorePrice.MulInt(sdkmath.NewIntFromUint64(chargeSize)).TruncateInt()
+	if primaryRate.IsPositive() {
+		primaryTotalFlowRate = primaryRate
+		err = k.paymentKeeper.Withdraw(ctx, paymentAddr, sdk.MustAccAddressFromHex(gvgFamily.VirtualPaymentAddress),
+			primaryTotalFlowRate.MulRaw(timeToPay))
+		if err != nil {
+			return fmt.Errorf("fail to pay GVG family: %s", err)
+		}
+	}
+
+	// secondary sp total rate
+	secondaryTotalFlowRate := sdk.ZeroInt()
+
+	var lvg *storagetypes.LocalVirtualGroup
+	for _, l := range internalBucketInfo.LocalVirtualGroups {
+		if l.Id == objectInfo.LocalVirtualGroupId {
+			lvg = l
+			break
+		}
+	}
+
+	gvg, found := k.virtualGroupKeeper.GetGVG(ctx, lvg.GlobalVirtualGroupId)
+	if !found {
+		return fmt.Errorf("get GVG failed: %d, %s", lvg.GlobalVirtualGroupId, lvg.String())
+	}
+
+	secondaryRate := price.SecondaryStorePrice.MulInt(sdkmath.NewIntFromUint64(chargeSize)).TruncateInt()
+	secondaryRate = secondaryRate.MulRaw(int64(len(gvg.SecondarySpIds)))
+	if secondaryRate.IsPositive() {
+		secondaryTotalFlowRate = secondaryTotalFlowRate.Add(secondaryRate)
+		err = k.paymentKeeper.Withdraw(ctx, paymentAddr, sdk.MustAccAddressFromHex(gvg.VirtualPaymentAddress),
+			secondaryTotalFlowRate.MulRaw(timeToPay))
+		if err != nil {
+			return fmt.Errorf("fail to pay GVG: %s", err)
+		}
+	}
+
+	// validator tax rate
+	versionedParams, err := k.paymentKeeper.GetVersionedParamsWithTs(ctx, internalBucketInfo.PriceTime)
+	if err != nil {
+		return fmt.Errorf("failed to get validator tax rate: %w, time: %d", err, internalBucketInfo.PriceTime)
+	}
+	validatorTaxRate := versionedParams.ValidatorTaxRate.MulInt(primaryTotalFlowRate.Add(secondaryTotalFlowRate)).TruncateInt()
+	if validatorTaxRate.IsPositive() {
+		err = k.paymentKeeper.Withdraw(ctx, paymentAddr, types.ValidatorTaxPoolAddress,
+			validatorTaxRate.MulRaw(timeToPay))
+		if err != nil {
+			return fmt.Errorf("fail to pay validator: %s", err)
+		}
+	}
+
+	return nil
 }
 
-func (k Keeper) ChargeViaBucketChange(ctx sdk.Context, bucketInfo *storagetypes.BucketInfo, changeFunc func(bucketInfo *storagetypes.BucketInfo) error) error {
+func (k Keeper) ChargeViaBucketChange(ctx sdk.Context, bucketInfo *storagetypes.BucketInfo,
+	internalBucketInfo *storagetypes.InternalBucketInfo,
+	changeFunc func(bi *storagetypes.BucketInfo, ibi *storagetypes.InternalBucketInfo) error) error {
+
 	// get previous bill
-	prevBill, err := k.GetBucketBill(ctx, bucketInfo)
+	prevBill, err := k.GetBucketReadStoreBill(ctx, bucketInfo, internalBucketInfo)
 	if err != nil {
-		return fmt.Errorf("get bucket bill failed: %w", err)
+		return fmt.Errorf("charge via bucket change failed, get bucket bill failed, bucket: %s, err: %s", bucketInfo.BucketName, err.Error())
 	}
-	// change bucket billing info
-	if err = changeFunc(bucketInfo); err != nil {
-		return errors.Wrapf(err, "change bucket billing info failed")
+	// change bucket internal info
+	if err = changeFunc(bucketInfo, internalBucketInfo); err != nil {
+		return errors.Wrapf(err, "change bucket internal info failed")
 	}
-	bucketInfo.BillingInfo.PriceTime = ctx.BlockTime().Unix()
+
 	// calculate new bill
-	newBill, err := k.GetBucketBill(ctx, bucketInfo)
+	internalBucketInfo.PriceTime = ctx.BlockTime().Unix()
+	newBill, err := k.GetBucketReadStoreBill(ctx, bucketInfo, internalBucketInfo)
 	if err != nil {
 		return fmt.Errorf("get new bucket bill failed: %w", err)
 	}
+
 	// charge according to bill change
-	err = k.ChargeAccordingToBillChange(ctx, prevBill, newBill)
+	err = k.ApplyBillChanges(ctx, prevBill, newBill)
 	if err != nil {
-		return fmt.Errorf("charge according to bill change failed: %w", err)
+		ctx.Logger().Error("charge via bucket change failed", "err", err.Error())
+		return err
 	}
 	return nil
 }
 
-func (k Keeper) GetBucketBill(ctx sdk.Context, bucketInfo *storagetypes.BucketInfo) (userFlows types.UserFlows, err error) {
-	userFlows.From = sdk.MustAccAddressFromHex(bucketInfo.PaymentAddress)
-	if bucketInfo.BillingInfo.TotalChargeSize == 0 && bucketInfo.ChargedReadQuota == 0 {
-		return userFlows, nil
+func (k Keeper) ChargeViaObjectChange(ctx sdk.Context, bucketInfo *storagetypes.BucketInfo,
+	internalBucketInfo *storagetypes.InternalBucketInfo, objectInfo *storagetypes.ObjectInfo, chargeSize uint64, delete bool) error {
+	userFlows := types.UserFlows{
+		From:  sdk.MustAccAddressFromHex(bucketInfo.PaymentAddress),
+		Flows: make([]types.OutFlow, 0),
 	}
+
 	price, err := k.paymentKeeper.GetStoragePrice(ctx, types.StoragePriceParams{
-		PrimarySp: bucketInfo.PrimarySpAddress,
-		PriceTime: bucketInfo.BillingInfo.PriceTime,
+		PrimarySp: bucketInfo.PrimarySpId,
+		PriceTime: internalBucketInfo.PriceTime,
 	})
 	if err != nil {
-		return userFlows, fmt.Errorf("get storage price failed: %w", err)
+		return fmt.Errorf("get storage price failed: %w", err)
 	}
-	primarySpFundingAddr, err := k.GetFundingAddressBySpAddr(ctx, sdk.MustAccAddressFromHex(bucketInfo.PrimarySpAddress))
-	if err != nil {
-		return userFlows, fmt.Errorf("get funding address by sp address failed: %w, sp: %s", err, bucketInfo.PrimarySpAddress)
+
+	gvgFamily, found := k.virtualGroupKeeper.GetGVGFamily(ctx, bucketInfo.PrimarySpId, bucketInfo.GlobalVirtualGroupFamilyId)
+	if !found {
+		return fmt.Errorf("get GVG family failed: %d", bucketInfo.GlobalVirtualGroupFamilyId)
 	}
-	totalUserOutRate := sdkmath.ZeroInt()
-	readFlowRate := price.ReadPrice.MulInt(sdkmath.NewIntFromUint64(bucketInfo.ChargedReadQuota)).TruncateInt()
-	primaryStoreFlowRate := price.PrimaryStorePrice.MulInt(sdkmath.NewIntFromUint64(bucketInfo.BillingInfo.TotalChargeSize)).TruncateInt()
-	primarySpRate := readFlowRate.Add(primaryStoreFlowRate)
-	if primarySpRate.IsPositive() {
+
+	// primary sp total rate
+	primaryTotalFlowRate := sdk.ZeroInt()
+
+	// secondary sp total rate
+	secondaryTotalFlowRate := sdk.ZeroInt()
+
+	var lvg *storagetypes.LocalVirtualGroup
+	for _, l := range internalBucketInfo.LocalVirtualGroups {
+		if l.Id == objectInfo.LocalVirtualGroupId {
+			lvg = l
+			break
+		}
+	}
+
+	// primary sp
+	primaryRate := price.PrimaryStorePrice.MulInt(sdkmath.NewIntFromUint64(chargeSize)).TruncateInt()
+	if primaryRate.IsPositive() {
+		primaryTotalFlowRate = primaryTotalFlowRate.Add(primaryRate)
+	}
+
+	//secondary sp
+	gvg, found := k.virtualGroupKeeper.GetGVG(ctx, lvg.GlobalVirtualGroupId)
+	if !found {
+		return fmt.Errorf("get GVG failed: %d, %s", lvg.GlobalVirtualGroupId, lvg.String())
+	}
+
+	secondaryRate := price.SecondaryStorePrice.MulInt(sdkmath.NewIntFromUint64(chargeSize)).TruncateInt()
+	secondaryRate = secondaryRate.MulRaw(int64(len(gvg.SecondarySpIds)))
+	if secondaryRate.IsPositive() {
 		userFlows.Flows = append(userFlows.Flows, types.OutFlow{
-			ToAddress: primarySpFundingAddr,
-			Rate:      primarySpRate,
+			ToAddress: gvg.VirtualPaymentAddress,
+			Rate:      secondaryRate,
 		})
-		totalUserOutRate = totalUserOutRate.Add(primarySpRate)
+		secondaryTotalFlowRate = secondaryTotalFlowRate.Add(secondaryRate)
 	}
-	for _, spObjectsSize := range bucketInfo.BillingInfo.SecondarySpObjectsSize {
-		rate := price.SecondaryStorePrice.MulInt(sdkmath.NewIntFromUint64(spObjectsSize.TotalChargeSize)).TruncateInt()
-		if rate.IsZero() {
-			continue
-		}
-		spFundingAddr, err := k.GetFundingAddressBySpAddr(ctx, sdk.MustAccAddressFromHex(spObjectsSize.SpAddress))
-		if err != nil {
-			return userFlows, fmt.Errorf("get funding address by sp address failed: %w, sp: %s", err, spObjectsSize.SpAddress)
-		}
+
+	if primaryTotalFlowRate.IsPositive() {
 		userFlows.Flows = append(userFlows.Flows, types.OutFlow{
-			ToAddress: spFundingAddr,
-			Rate:      rate,
+			ToAddress: gvgFamily.VirtualPaymentAddress,
+			Rate:      primaryTotalFlowRate,
 		})
-		totalUserOutRate = totalUserOutRate.Add(rate)
 	}
-	versionedParams, err := k.paymentKeeper.GetVersionedParamsWithTs(ctx, bucketInfo.BillingInfo.PriceTime)
+
+	versionedParams, err := k.paymentKeeper.GetVersionedParamsWithTs(ctx, internalBucketInfo.PriceTime)
 	if err != nil {
-		return userFlows, fmt.Errorf("failed to get validator tax rate: %w, time: %d", err, bucketInfo.BillingInfo.PriceTime)
+		return fmt.Errorf("failed to get validator tax rate: %w, time: %d", err, internalBucketInfo.PriceTime)
 	}
-	validatorTaxRate := versionedParams.ValidatorTaxRate.MulInt(totalUserOutRate).TruncateInt()
+	validatorTaxRate := versionedParams.ValidatorTaxRate.MulInt(primaryTotalFlowRate.Add(secondaryTotalFlowRate)).TruncateInt()
 	if validatorTaxRate.IsPositive() {
 		userFlows.Flows = append(userFlows.Flows, types.OutFlow{
 			ToAddress: types.ValidatorTaxPoolAddress.String(),
 			Rate:      validatorTaxRate,
 		})
 	}
-	return userFlows, nil
-}
 
-func (k Keeper) ChargeAccordingToBillChange(ctx sdk.Context, prev, current types.UserFlows) error {
-	prev.Flows = GetNegFlows(prev.Flows)
-	err := k.paymentKeeper.ApplyUserFlowsList(ctx, []types.UserFlows{prev, current})
+	if !delete {
+		internalBucketInfo.TotalChargeSize = internalBucketInfo.TotalChargeSize + chargeSize
+		lvg.TotalChargeSize = lvg.TotalChargeSize + chargeSize
+	} else {
+		internalBucketInfo.TotalChargeSize = internalBucketInfo.TotalChargeSize - chargeSize
+		lvg.TotalChargeSize = lvg.TotalChargeSize - chargeSize
+
+		userFlows.Flows = getNegFlows(userFlows.Flows)
+	}
+	err = k.paymentKeeper.ApplyUserFlowsList(ctx, []types.UserFlows{userFlows})
 	if err != nil {
-		return fmt.Errorf("apply user flows list failed: %w", err)
+		ctx.Logger().Error("charge object store fee failed", "err", err.Error())
+		return err
 	}
+
 	return nil
 }
 
-func (k Keeper) ChargeDeleteObject(ctx sdk.Context, bucketInfo *storagetypes.BucketInfo, objectInfo *storagetypes.ObjectInfo) error {
-	chargeSize, err := k.GetChargeSize(ctx, objectInfo.PayloadSize, objectInfo.CreateAt)
+func (k Keeper) GetBucketReadStoreBill(ctx sdk.Context, bucketInfo *storagetypes.BucketInfo,
+	internalBucketInfo *storagetypes.InternalBucketInfo) (userFlows types.UserFlows, err error) {
+	userFlows.From = sdk.MustAccAddressFromHex(bucketInfo.PaymentAddress)
+	if internalBucketInfo.TotalChargeSize == 0 && bucketInfo.ChargedReadQuota == 0 {
+		return userFlows, nil
+	}
+	price, err := k.paymentKeeper.GetStoragePrice(ctx, types.StoragePriceParams{
+		PrimarySp: bucketInfo.PrimarySpId,
+		PriceTime: internalBucketInfo.PriceTime,
+	})
 	if err != nil {
-		return fmt.Errorf("get charge size error: %w", err)
+		return userFlows, fmt.Errorf("get storage price failed: %w", err)
 	}
-	return k.ChargeViaBucketChange(ctx, bucketInfo, func(bi *storagetypes.BucketInfo) error {
-		bi.BillingInfo.TotalChargeSize -= chargeSize
-		var toBeSub []storagetypes.SecondarySpObjectsSize
-		for _, sp := range objectInfo.SecondarySpAddresses {
-			toBeSub = append(toBeSub, storagetypes.SecondarySpObjectsSize{
-				SpAddress:       sp,
-				TotalChargeSize: chargeSize,
-			})
+
+	gvgFamily, found := k.virtualGroupKeeper.GetGVGFamily(ctx, bucketInfo.PrimarySpId, bucketInfo.GlobalVirtualGroupFamilyId)
+	if !found {
+		return userFlows, fmt.Errorf("get GVG family failed: %d", bucketInfo.GlobalVirtualGroupFamilyId)
+	}
+
+	// primary sp total rate
+	primaryTotalFlowRate := price.ReadPrice.MulInt(sdkmath.NewIntFromUint64(bucketInfo.ChargedReadQuota)).TruncateInt()
+
+	// secondary sp total rate
+	secondaryTotalFlowRate := sdk.ZeroInt()
+
+	for _, lvg := range internalBucketInfo.LocalVirtualGroups {
+		// primary sp
+		primaryRate := price.PrimaryStorePrice.MulInt(sdkmath.NewIntFromUint64(lvg.TotalChargeSize)).TruncateInt()
+		if primaryRate.IsPositive() {
+			primaryTotalFlowRate = primaryTotalFlowRate.Add(primaryRate)
 		}
-		bi.BillingInfo.SecondarySpObjectsSize = SubSecondarySpObjectsSize(bi.BillingInfo.SecondarySpObjectsSize, toBeSub)
-		return nil
-	})
-}
 
-func GetNegFlows(flows []types.OutFlow) (negFlows []types.OutFlow) {
-	negFlows = make([]types.OutFlow, len(flows))
-	for i, flow := range flows {
-		negFlows[i] = types.OutFlow{ToAddress: flow.ToAddress, Rate: flow.Rate.Neg()}
+		//secondary sp
+		gvg, found := k.virtualGroupKeeper.GetGVG(ctx, lvg.GlobalVirtualGroupId)
+		if !found {
+			return userFlows, fmt.Errorf("get GVG failed: %d, %s", lvg.GlobalVirtualGroupId, lvg.String())
+		}
+
+		secondaryRate := price.SecondaryStorePrice.MulInt(sdkmath.NewIntFromUint64(lvg.TotalChargeSize)).TruncateInt()
+		secondaryRate = secondaryRate.MulRaw(int64(len(gvg.SecondarySpIds)))
+		if secondaryRate.IsPositive() {
+			userFlows.Flows = append(userFlows.Flows, types.OutFlow{
+				ToAddress: gvg.VirtualPaymentAddress,
+				Rate:      secondaryRate,
+			})
+			secondaryTotalFlowRate = secondaryTotalFlowRate.Add(secondaryRate)
+		}
 	}
-	return negFlows
-}
 
-func MergeSecondarySpObjectsSize(list []storagetypes.SecondarySpObjectsSize) []storagetypes.SecondarySpObjectsSize {
-	if len(list) <= 1 {
-		return list
+	if primaryTotalFlowRate.IsPositive() {
+		userFlows.Flows = append(userFlows.Flows, types.OutFlow{
+			ToAddress: gvgFamily.VirtualPaymentAddress,
+			Rate:      primaryTotalFlowRate,
+		})
 	}
-	helperMap := make(map[string]uint64)
-	for _, spObjectsSize := range list {
-		helperMap[spObjectsSize.SpAddress] += spObjectsSize.TotalChargeSize
+
+	versionedParams, err := k.paymentKeeper.GetVersionedParamsWithTs(ctx, internalBucketInfo.PriceTime)
+	if err != nil {
+		return userFlows, fmt.Errorf("failed to get validator tax rate: %w, time: %d", err, internalBucketInfo.PriceTime)
 	}
-	res := make([]storagetypes.SecondarySpObjectsSize, 0, len(helperMap))
-	for sp, size := range helperMap {
-		if size == 0 {
-			continue
-		}
-		res = append(res, storagetypes.SecondarySpObjectsSize{
-			SpAddress:       sp,
-			TotalChargeSize: size,
+	validatorTaxRate := versionedParams.ValidatorTaxRate.MulInt(primaryTotalFlowRate.Add(secondaryTotalFlowRate)).TruncateInt()
+	if validatorTaxRate.IsPositive() {
+		userFlows.Flows = append(userFlows.Flows, types.OutFlow{
+			ToAddress: types.ValidatorTaxPoolAddress.String(),
+			Rate:      validatorTaxRate,
 		})
 	}
-	sort.Slice(res, func(i, j int) bool {
-		return res[i].SpAddress < res[j].SpAddress
-	})
-	return res
+
+	return userFlows, nil
 }
 
-func SubSecondarySpObjectsSize(prev []storagetypes.SecondarySpObjectsSize, toBeSub []storagetypes.SecondarySpObjectsSize) []storagetypes.SecondarySpObjectsSize {
-	if len(toBeSub) == 0 {
-		return prev
+func (k Keeper) UnChargeBucketReadStoreFee(ctx sdk.Context, bucketInfo *storagetypes.BucketInfo,
+	internalBucketInfo *storagetypes.InternalBucketInfo) error {
+	bill, err := k.GetBucketReadStoreBill(ctx, bucketInfo, internalBucketInfo)
+	if err != nil {
+		return fmt.Errorf("get bucket bill failed, bucket: %s, err: %s", bucketInfo.BucketName, err.Error())
 	}
-	helperMap := make(map[string]uint64)
-	// merge prev
-	for _, spObjectsSize := range prev {
-		helperMap[spObjectsSize.SpAddress] += spObjectsSize.TotalChargeSize
+	bill.Flows = getNegFlows(bill.Flows)
+	err = k.paymentKeeper.ApplyUserFlowsList(ctx, []types.UserFlows{bill})
+	if err != nil {
+		return fmt.Errorf("apply user flows list failed: %w", err)
 	}
-	// sub toBeSub
-	for _, spObjectsSize := range toBeSub {
-		helperMap[spObjectsSize.SpAddress] -= spObjectsSize.TotalChargeSize
+	return nil
+}
+
+func (k Keeper) ChargeBucketReadStoreFee(ctx sdk.Context, bucketInfo *storagetypes.BucketInfo,
+	internalBucketInfo *storagetypes.InternalBucketInfo) error {
+	internalBucketInfo.PriceTime = ctx.BlockTime().Unix()
+	bill, err := k.GetBucketReadStoreBill(ctx, bucketInfo, internalBucketInfo)
+	if err != nil {
+		return fmt.Errorf("get bucket bill failed, bucket: %s, err: %s", bucketInfo.BucketName, err.Error())
 	}
-	// merge the result
-	res := make([]storagetypes.SecondarySpObjectsSize, 0, len(helperMap))
-	for sp, size := range helperMap {
-		if size == 0 {
-			continue
-		}
-		res = append(res, storagetypes.SecondarySpObjectsSize{
-			SpAddress:       sp,
-			TotalChargeSize: size,
-		})
+	err = k.paymentKeeper.ApplyUserFlowsList(ctx, []types.UserFlows{bill})
+	if err != nil {
+		return fmt.Errorf("apply user flows list failed: %w", err)
 	}
-	sort.Slice(res, func(i, j int) bool {
-		return res[i].SpAddress < res[j].SpAddress
-	})
-	return res
+	return nil
+}
+
+func (k Keeper) ApplyBillChanges(ctx sdk.Context, prevFlows, currentFlows types.UserFlows) error {
+	prevFlows.Flows = getNegFlows(prevFlows.Flows)
+	err := k.paymentKeeper.ApplyUserFlowsList(ctx, []types.UserFlows{prevFlows, currentFlows})
+	if err != nil {
+		return fmt.Errorf("apply user flows list failed: %w", err)
+	}
+	return nil
+}
+
+func getNegFlows(flows []types.OutFlow) (negFlows []types.OutFlow) {
+	negFlows = make([]types.OutFlow, len(flows))
+	for i, flow := range flows {
+		negFlows[i] = types.OutFlow{ToAddress: flow.ToAddress, Rate: flow.Rate.Neg()}
+	}
+	return negFlows
 }
 
-func (k Keeper) GetObjectLockFee(ctx sdk.Context, primarySpAddress string, priceTime int64, payloadSize uint64) (amount sdkmath.Int, err error) {
+func (k Keeper) GetObjectLockFee(ctx sdk.Context, primarySpId uint32, priceTime int64, payloadSize uint64) (amount sdkmath.Int, err error) {
 	price, err := k.paymentKeeper.GetStoragePrice(ctx, types.StoragePriceParams{
-		PrimarySp: primarySpAddress,
+		PrimarySp: primarySpId,
 		PriceTime: priceTime,
 	})
 	if err != nil {
 		return amount, fmt.Errorf("get store price failed: %w", err)
 	}
-	chargeSize, err := k.GetChargeSize(ctx, payloadSize, priceTime)
+	chargeSize, err := k.GetObjectChargeSize(ctx, payloadSize, priceTime)
 	if err != nil {
 		return amount, fmt.Errorf("get charge size error: %w", err)
 	}
-	secondarySPNum := int64(k.GetExpectSecondarySPNumForECObject(ctx))
+	secondarySPNum := int64(k.GetExpectSecondarySPNumForECObject(ctx, priceTime))
 	rate := price.PrimaryStorePrice.Add(price.SecondaryStorePrice.MulInt64(secondarySPNum)).MulInt(sdkmath.NewIntFromUint64(chargeSize)).TruncateInt()
 	versionedParams, err := k.paymentKeeper.GetVersionedParamsWithTs(ctx, priceTime)
 	if err != nil {
@@ -321,7 +602,7 @@ func (k Keeper) GetObjectLockFee(ctx sdk.Context, primarySpAddress string, price
 	return amount, nil
 }
 
-func (k Keeper) GetChargeSize(ctx sdk.Context, payloadSize uint64, ts int64) (size uint64, err error) {
+func (k Keeper) GetObjectChargeSize(ctx sdk.Context, payloadSize uint64, ts int64) (size uint64, err error) {
 	params, err := k.GetVersionedParamsWithTs(ctx, ts)
 	if err != nil {
 		return size, fmt.Errorf("get charge size failed, ts:%d, error: %w", ts, err)
diff --git a/x/storage/keeper/payment_test.go b/x/storage/keeper/payment_test.go
index 66c5af5c9..477aac26c 100644
--- a/x/storage/keeper/payment_test.go
+++ b/x/storage/keeper/payment_test.go
@@ -4,157 +4,254 @@ import (
 	"testing"
 	"time"
 
-	sdkmath "cosmossdk.io/math"
+	"github.com/cosmos/cosmos-sdk/baseapp"
+	"github.com/cosmos/cosmos-sdk/codec"
+	storetypes "github.com/cosmos/cosmos-sdk/store/types"
+	"github.com/cosmos/cosmos-sdk/testutil"
 	sdk "github.com/cosmos/cosmos-sdk/types"
+	moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
 	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
-	"github.com/samber/lo"
+	govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
+	"github.com/golang/mock/gomock"
 	"github.com/stretchr/testify/suite"
 
-	keepertest "github.com/bnb-chain/greenfield/testutil/keeper"
 	"github.com/bnb-chain/greenfield/testutil/sample"
+	"github.com/bnb-chain/greenfield/x/challenge"
 	paymenttypes "github.com/bnb-chain/greenfield/x/payment/types"
 	sptypes "github.com/bnb-chain/greenfield/x/sp/types"
 	"github.com/bnb-chain/greenfield/x/storage/keeper"
 	"github.com/bnb-chain/greenfield/x/storage/types"
+	virtualgroupmoduletypes "github.com/bnb-chain/greenfield/x/virtualgroup/types"
 )
 
-type IntegrationTestSuiteWithoutMock struct {
+type TestSuite struct {
 	suite.Suite
 
-	keeper               *keeper.Keeper
-	depKeepers           keepertest.StorageDepKeepers
-	ctx                  sdk.Context
-	PrimarySpAddr        sdk.AccAddress
-	PrimarySpFundingAddr sdk.AccAddress
-	PrimarySp            sptypes.StorageProvider
-	SecondarySps         []sptypes.StorageProvider
-	UserAddr             sdk.AccAddress
-	Denom                string
+	cdc           codec.Codec
+	storageKeeper *keeper.Keeper
+
+	accountKeeper      *types.MockAccountKeeper
+	spKeeper           *types.MockSpKeeper
+	permissionKeeper   *types.MockPermissionKeeper
+	crosschainKeeper   *types.MockCrossChainKeeper
+	paymentKeeper      *types.MockPaymentKeeper
+	virtualGroupKeeper *types.MockVirtualGroupKeeper
+
+	ctx         sdk.Context
+	queryClient types.QueryClient
+	msgServer   types.MsgServer
 }
 
-func (s *IntegrationTestSuiteWithoutMock) SetupTest() {
-	s.Denom = "BNB"
-	s.keeper, s.depKeepers, s.ctx = keepertest.StorageKeeper(s.T())
-	ctx := s.ctx.WithBlockTime(time.Now())
-	// init data
-	s.PrimarySpAddr = sample.RandAccAddress()
-	s.PrimarySpFundingAddr = sample.RandAccAddress()
-	s.UserAddr = sample.RandAccAddress()
-	sp := sptypes.StorageProvider{
-		OperatorAddress: s.PrimarySpAddr.String(),
-		FundingAddress:  s.PrimarySpFundingAddr.String(),
-	}
-	s.depKeepers.SpKeeper.SetStorageProvider(ctx, &sp)
-	for i := 0; i < 6; i++ {
-		secondarySpAddr := sample.RandAccAddress()
-		secondarySpFundingAddr := sample.RandAccAddress()
-		secondarySp := sptypes.StorageProvider{
-			OperatorAddress: secondarySpAddr.String(),
-			FundingAddress:  secondarySpFundingAddr.String(),
-		}
-		s.SecondarySps = append(s.SecondarySps, secondarySp)
-		s.depKeepers.SpKeeper.SetStorageProvider(ctx, &secondarySp)
+func (s *TestSuite) SetupTest() {
+	encCfg := moduletestutil.MakeTestEncodingConfig(challenge.AppModuleBasic{})
+	key := storetypes.NewKVStoreKey(types.StoreKey)
+	testCtx := testutil.DefaultContextWithDB(s.T(), key, storetypes.NewTransientStoreKey("transient_test"))
+	header := testCtx.Ctx.BlockHeader()
+	header.Time = time.Now()
+	testCtx = testutil.TestContext{
+		Ctx: sdk.NewContext(testCtx.CMS, header, false, nil, testCtx.Ctx.Logger()),
+		DB:  testCtx.DB,
+		CMS: testCtx.CMS,
 	}
-	s.depKeepers.SpKeeper.SetSpStoragePrice(ctx, sptypes.SpStoragePrice{
-		SpAddress:     s.PrimarySpAddr.String(),
-		UpdateTimeSec: 1,
-		ReadPrice:     sdk.NewDec(2),
-		StorePrice:    sdk.NewDec(5),
-		FreeReadQuota: 10000,
-	})
-	s.depKeepers.SpKeeper.SetSecondarySpStorePrice(ctx, sptypes.SecondarySpStorePrice{
-		UpdateTimeSec: 1,
-		StorePrice:    sdk.NewDec(4),
-	})
-	coins := sdk.Coins{sdk.Coin{Denom: s.Denom, Amount: sdkmath.NewInt(1e18)}}
-	bankKeeper := s.depKeepers.BankKeeper
-	balances := bankKeeper.GetAllBalances(ctx, s.depKeepers.AccountKeeper.GetModuleAddress(authtypes.Minter))
-	s.T().Logf("Minter module balances: %s", balances)
-	err := bankKeeper.SendCoinsFromModuleToAccount(ctx, authtypes.Minter, s.UserAddr, coins)
+	s.ctx = testCtx.Ctx
+
+	ctrl := gomock.NewController(s.T())
+
+	accountKeeper := types.NewMockAccountKeeper(ctrl)
+	spKeeper := types.NewMockSpKeeper(ctrl)
+	permissionKeeper := types.NewMockPermissionKeeper(ctrl)
+	crosschainKeeper := types.NewMockCrossChainKeeper(ctrl)
+	paymentKeeper := types.NewMockPaymentKeeper(ctrl)
+	virtualGroupKeeper := types.NewMockVirtualGroupKeeper(ctrl)
+
+	s.storageKeeper = keeper.NewKeeper(
+		encCfg.Codec,
+		key,
+		key,
+		accountKeeper,
+		spKeeper,
+		paymentKeeper,
+		permissionKeeper,
+		crosschainKeeper,
+		virtualGroupKeeper,
+		authtypes.NewModuleAddress(govtypes.ModuleName).String(),
+	)
+
+	s.cdc = encCfg.Codec
+	s.accountKeeper = accountKeeper
+	s.spKeeper = spKeeper
+	s.permissionKeeper = permissionKeeper
+	s.crosschainKeeper = crosschainKeeper
+	s.paymentKeeper = paymentKeeper
+	s.virtualGroupKeeper = virtualGroupKeeper
+
+	err := s.storageKeeper.SetParams(s.ctx, types.DefaultParams())
 	s.Require().NoError(err)
-	balance := bankKeeper.GetBalance(ctx, s.UserAddr, "BNB")
-	s.T().Logf("s.UserAddr: %s, balance: %s", s.UserAddr, balance)
+
+	queryHelper := baseapp.NewQueryServerTestHelper(testCtx.Ctx, encCfg.InterfaceRegistry)
+	types.RegisterQueryServer(queryHelper, s.storageKeeper)
+
+	s.queryClient = types.NewQueryClient(queryHelper)
+	s.msgServer = keeper.NewMsgServerImpl(*s.storageKeeper)
 }
 
-func (s *IntegrationTestSuiteWithoutMock) TestCreateBucket_Payment() {
-	ctx := s.ctx.WithBlockTime(time.Now())
-	// mock create bucket
-	ChargedReadQuota := uint64(1000)
-	bucket := types.BucketInfo{
-		ChargedReadQuota: ChargedReadQuota,
-		PaymentAddress:   s.UserAddr.String(),
-		PrimarySpAddress: s.PrimarySpAddr.String(),
+func TestTestSuite(t *testing.T) {
+	suite.Run(t, new(TestSuite))
+}
+
+func (s *TestSuite) TestGetObjectLockFee() {
+	primarySp := &sptypes.StorageProvider{Status: sptypes.STATUS_IN_SERVICE, Id: 100, OperatorAddress: sample.RandAccAddress().String()}
+	s.spKeeper.EXPECT().GetStorageProvider(gomock.Any(), gomock.Eq(primarySp.Id)).
+		Return(primarySp, true).AnyTimes()
+
+	price := paymenttypes.StoragePrice{
+		ReadPrice:           sdk.NewDec(100),
+		PrimaryStorePrice:   sdk.NewDec(1000),
+		SecondaryStorePrice: sdk.NewDec(500),
 	}
-	t1 := int64(200)
-	ctx = ctx.WithBlockTime(ctx.BlockTime().Add(time.Duration(t1) * time.Second))
-	err := s.keeper.ChargeInitialReadFee(ctx, &bucket)
+	s.paymentKeeper.EXPECT().GetStoragePrice(gomock.Any(), gomock.Any()).
+		Return(price, nil).AnyTimes()
+	params := paymenttypes.DefaultParams()
+	s.paymentKeeper.EXPECT().GetParams(gomock.Any()).
+		Return(params).AnyTimes()
+	s.paymentKeeper.EXPECT().GetVersionedParamsWithTs(gomock.Any(), gomock.Any()).
+		Return(params.VersionedParams, nil).AnyTimes()
+
+	// verify lock fee calculation
+	payloadSize := int64(10 * 1024 * 1024)
+	amount, err := s.storageKeeper.GetObjectLockFee(s.ctx, 100, time.Now().Unix(), uint64(payloadSize))
 	s.Require().NoError(err)
-	userStreamRecordCreateBucket, found := s.depKeepers.PaymentKeeper.GetStreamRecord(ctx, s.UserAddr)
-	s.Require().True(found)
-	s.T().Logf("userStreamRecordCreateBucket: %+v", userStreamRecordCreateBucket)
-	spStreamRecordCreateBucket, found := s.depKeepers.PaymentKeeper.GetStreamRecord(ctx, s.PrimarySpFundingAddr)
-	s.Require().True(found)
-	s.T().Logf("spStreamRecordCreateBucket: %+v", spStreamRecordCreateBucket)
-
-	// mock add a object
-	t2 := t1 + 5000
-	ctx = ctx.WithBlockTime(ctx.BlockTime().Add(time.Duration(t2) * time.Second))
-	bucket.BillingInfo.PriceTime = ctx.BlockTime().Unix()
-	object := types.ObjectInfo{
-		PayloadSize: 100,
-		CreateAt:    ctx.BlockTime().Unix(),
+	secondarySPNum := int64(s.storageKeeper.GetExpectSecondarySPNumForECObject(s.ctx, time.Now().Unix()))
+	expectedAmount := price.PrimaryStorePrice.Add(price.SecondaryStorePrice.MulInt64(secondarySPNum)).
+		MulInt64(payloadSize).MulInt64(int64(params.VersionedParams.ReserveTime)).TruncateInt()
+	s.Require().True(amount.Equal(expectedAmount))
+}
+
+func (s *TestSuite) TestGetBucketBill() {
+	gvgFamily := &virtualgroupmoduletypes.GlobalVirtualGroupFamily{
+		Id:                    1,
+		VirtualPaymentAddress: sample.RandAccAddress().String(),
 	}
-	err = s.keeper.LockStoreFee(ctx, &bucket, &object)
-	s.Require().NoError(err)
-	s.T().Logf("create object")
-	userStreamRecordCreateObject, found := s.depKeepers.PaymentKeeper.GetStreamRecord(ctx, s.UserAddr)
-	s.Require().True(found)
-	s.T().Logf("userStreamRecordCreateObject: %+v", userStreamRecordCreateObject)
-	spStreamRecordCreateObject, found := s.depKeepers.PaymentKeeper.GetStreamRecord(ctx, s.PrimarySpFundingAddr)
-	s.Require().True(found)
-	s.T().Logf("spStreamRecordCreateObject: %+v", spStreamRecordCreateObject)
-
-	// mock seal object
-	secondarySpAddresses := lo.Map(s.SecondarySps, func(sp sptypes.StorageProvider, index int) string {
-		return sp.OperatorAddress
-	})
-	object.SecondarySpAddresses = secondarySpAddresses
-	err = s.keeper.UnlockAndChargeStoreFee(ctx, &bucket, &object)
-	s.Require().NoError(err)
-	s.T().Logf("seal object")
-	userStreamRecordSealObject, found := s.depKeepers.PaymentKeeper.GetStreamRecord(ctx, s.UserAddr)
-	s.Require().True(found)
-	s.T().Logf("userStreamRecordSealObject: %+v", userStreamRecordSealObject)
-	spStreamRecordSealObject, found := s.depKeepers.PaymentKeeper.GetStreamRecord(ctx, s.PrimarySpFundingAddr)
-	s.Require().True(found)
-	s.T().Logf("spStreamRecordSealObject: %+v", spStreamRecordSealObject)
-
-	// check
-	primaryStorePriceRes, err := s.depKeepers.SpKeeper.GetSpStoragePriceByTime(ctx, s.PrimarySpAddr, t2)
+	s.virtualGroupKeeper.EXPECT().GetGVGFamily(gomock.Any(), gomock.Any(), gomock.Any()).
+		Return(gvgFamily, true).AnyTimes()
+
+	primarySp := &sptypes.StorageProvider{
+		Status:          sptypes.STATUS_IN_SERVICE,
+		Id:              100,
+		OperatorAddress: sample.RandAccAddress().String(),
+		FundingAddress:  sample.RandAccAddress().String()}
+	s.spKeeper.EXPECT().GetStorageProvider(gomock.Any(), gomock.Eq(primarySp.Id)).
+		Return(primarySp, true).AnyTimes()
+
+	price := paymenttypes.StoragePrice{
+		ReadPrice:           sdk.NewDec(100),
+		PrimaryStorePrice:   sdk.NewDec(1000),
+		SecondaryStorePrice: sdk.NewDec(500),
+	}
+	s.paymentKeeper.EXPECT().GetStoragePrice(gomock.Any(), gomock.Any()).
+		Return(price, nil).AnyTimes()
+	params := paymenttypes.DefaultParams()
+	s.paymentKeeper.EXPECT().GetVersionedParamsWithTs(gomock.Any(), gomock.Any()).
+		Return(params.VersionedParams, nil).AnyTimes()
+
+	// empty bucket, zero read quota
+	bucketInfo := &types.BucketInfo{
+		Owner:                      "",
+		BucketName:                 "bucketname",
+		Id:                         sdk.NewUint(1),
+		PaymentAddress:             sample.RandAccAddress().String(),
+		PrimarySpId:                primarySp.Id,
+		GlobalVirtualGroupFamilyId: gvgFamily.Id,
+		ChargedReadQuota:           0,
+	}
+	internalBucketInfo := &types.InternalBucketInfo{}
+	flows, err := s.storageKeeper.GetBucketReadStoreBill(s.ctx, bucketInfo, internalBucketInfo)
 	s.Require().NoError(err)
-	s.T().Logf("primaryStorePriceRes: %+v", primaryStorePriceRes)
-	primarySpRateDiff := spStreamRecordSealObject.NetflowRate.Sub(spStreamRecordCreateBucket.NetflowRate)
-	expectedRate := primaryStorePriceRes.StorePrice.MulInt(sdk.NewIntFromUint64(bucket.BillingInfo.TotalChargeSize)).TruncateInt()
-	readRate := primaryStorePriceRes.ReadPrice.MulInt(sdk.NewIntFromUint64(ChargedReadQuota)).TruncateInt()
-	s.T().Logf("primarySpRateDiff: %s, expectedRate: %s, readRate: %s", primarySpRateDiff, expectedRate, readRate)
-	s.Require().Equal(expectedRate.String(), primarySpRateDiff.String())
-
-	// force delete
-	err = s.depKeepers.PaymentKeeper.ForceSettle(ctx, userStreamRecordSealObject)
+	s.Require().True(len(flows.Flows) == 0)
+
+	// empty bucket
+	bucketInfo = &types.BucketInfo{
+		Owner:                      "",
+		BucketName:                 "bucketname",
+		Id:                         sdk.NewUint(1),
+		PaymentAddress:             sample.RandAccAddress().String(),
+		PrimarySpId:                primarySp.Id,
+		GlobalVirtualGroupFamilyId: gvgFamily.Id,
+		ChargedReadQuota:           100,
+	}
+	internalBucketInfo = &types.InternalBucketInfo{}
+	flows, err = s.storageKeeper.GetBucketReadStoreBill(s.ctx, bucketInfo, internalBucketInfo)
 	s.Require().NoError(err)
-	s.depKeepers.PaymentKeeper.SetStreamRecord(ctx, userStreamRecordSealObject)
-	userStreamRecordSealObject, found = s.depKeepers.PaymentKeeper.GetStreamRecord(ctx, s.UserAddr)
-	s.Require().True(found)
-	s.T().Logf("userStreamRecordSealObject: %+v", userStreamRecordSealObject)
-	s.Require().Equal(userStreamRecordSealObject.Status, paymenttypes.STREAM_ACCOUNT_STATUS_FROZEN)
-	bucket.ChargedReadQuota += 100000000
-	err = s.keeper.ChargeDeleteObject(ctx, &bucket, &object)
-	s.Require().ErrorContains(err, "is frozen")
-	ctx = ctx.WithValue(paymenttypes.ForceUpdateFrozenStreamRecordKey, true)
-	err = s.keeper.ChargeDeleteObject(ctx, &bucket, &object)
+	readRate := price.ReadPrice.MulInt64(int64(bucketInfo.ChargedReadQuota)).TruncateInt()
+	s.Require().Equal(flows.Flows[0].ToAddress, gvgFamily.VirtualPaymentAddress)
+	s.Require().Equal(flows.Flows[0].Rate, readRate)
+	taxPoolRate := params.VersionedParams.ValidatorTaxRate.MulInt(readRate).TruncateInt()
+	s.Require().Equal(flows.Flows[1].ToAddress, paymenttypes.ValidatorTaxPoolAddress.String())
+	s.Require().Equal(flows.Flows[1].Rate, taxPoolRate)
+
+	// none empty bucket
+	bucketInfo = &types.BucketInfo{
+		Owner:                      "",
+		BucketName:                 "bucketname",
+		Id:                         sdk.NewUint(1),
+		PaymentAddress:             sample.RandAccAddress().String(),
+		PrimarySpId:                primarySp.Id,
+		GlobalVirtualGroupFamilyId: gvgFamily.Id,
+		ChargedReadQuota:           100,
+	}
+
+	internalBucketInfo = &types.InternalBucketInfo{
+		TotalChargeSize: 300,
+		LocalVirtualGroups: []*types.LocalVirtualGroup{
+			{
+				Id:                   1,
+				TotalChargeSize:      100,
+				GlobalVirtualGroupId: 1,
+			}, {
+				Id:                   2,
+				TotalChargeSize:      200,
+				GlobalVirtualGroupId: 2,
+			},
+		},
+	}
+
+	gvg1 := &virtualgroupmoduletypes.GlobalVirtualGroup{
+		Id:                    1,
+		PrimarySpId:           primarySp.Id,
+		SecondarySpIds:        []uint32{101, 102, 103, 104, 105, 106},
+		VirtualPaymentAddress: sample.RandAccAddress().String(),
+	}
+	gvg2 := &virtualgroupmoduletypes.GlobalVirtualGroup{
+		Id:                    2,
+		PrimarySpId:           primarySp.Id,
+		SecondarySpIds:        []uint32{201, 202, 203, 204, 205, 206},
+		VirtualPaymentAddress: sample.RandAccAddress().String(),
+	}
+	s.virtualGroupKeeper.EXPECT().GetGVG(gomock.Any(), gvg1.Id).
+		Return(gvg1, true).AnyTimes()
+	s.virtualGroupKeeper.EXPECT().GetGVG(gomock.Any(), gvg2.Id).
+		Return(gvg2, true).AnyTimes()
+
+	flows, err = s.storageKeeper.GetBucketReadStoreBill(s.ctx, bucketInfo, internalBucketInfo)
 	s.Require().NoError(err)
-}
 
-func TestKeeperTestSuiteWithoutMock(t *testing.T) {
-	suite.Run(t, new(IntegrationTestSuiteWithoutMock))
+	gvg1StoreSize := internalBucketInfo.LocalVirtualGroups[0].TotalChargeSize * uint64(len(gvg1.SecondarySpIds))
+	gvg1StoreRate := price.SecondaryStorePrice.MulInt64(int64(gvg1StoreSize)).TruncateInt()
+	s.Require().Equal(flows.Flows[0].ToAddress, gvg1.VirtualPaymentAddress)
+	s.Require().Equal(flows.Flows[0].Rate, gvg1StoreRate)
+
+	gvg2StoreSize := internalBucketInfo.LocalVirtualGroups[1].TotalChargeSize * uint64(len(gvg2.SecondarySpIds))
+	gvg2StoreRate := price.SecondaryStorePrice.MulInt64(int64(gvg2StoreSize)).TruncateInt()
+	s.Require().Equal(flows.Flows[1].ToAddress, gvg2.VirtualPaymentAddress)
+	s.Require().Equal(flows.Flows[1].Rate, gvg2StoreRate)
+
+	readRate = price.ReadPrice.MulInt64(int64(bucketInfo.ChargedReadQuota)).TruncateInt()
+	primaryStoreRate := price.PrimaryStorePrice.MulInt64(int64(internalBucketInfo.TotalChargeSize)).TruncateInt()
+	s.Require().Equal(flows.Flows[2].ToAddress, gvgFamily.VirtualPaymentAddress)
+	s.Require().Equal(flows.Flows[2].Rate, readRate.Add(primaryStoreRate))
+
+	totalRate := readRate.Add(primaryStoreRate).Add(gvg1StoreRate).Add(gvg2StoreRate)
+	taxPoolRate = params.VersionedParams.ValidatorTaxRate.MulInt(totalRate).TruncateInt()
+	s.Require().Equal(flows.Flows[3].ToAddress, paymenttypes.ValidatorTaxPoolAddress.String())
+	s.Require().Equal(flows.Flows[3].Rate, taxPoolRate)
 }
diff --git a/x/storage/keeper/query.go b/x/storage/keeper/query.go
index caa142279..f18e900e1 100644
--- a/x/storage/keeper/query.go
+++ b/x/storage/keeper/query.go
@@ -14,8 +14,10 @@ import (
 
 	"github.com/bnb-chain/greenfield/internal/sequence"
 	gnfd "github.com/bnb-chain/greenfield/types"
+	errors2 "github.com/bnb-chain/greenfield/types/errors"
 	permtypes "github.com/bnb-chain/greenfield/x/permission/types"
 	"github.com/bnb-chain/greenfield/x/storage/types"
+	types2 "github.com/bnb-chain/greenfield/x/virtualgroup/types"
 )
 
 func (k Keeper) HeadBucket(goCtx context.Context, req *types.QueryHeadBucketRequest) (*types.QueryHeadBucketResponse, error) {
@@ -62,13 +64,27 @@ func (k Keeper) HeadObject(goCtx context.Context, req *types.QueryHeadObjectRequ
 
 	ctx := sdk.UnwrapSDKContext(goCtx)
 
-	objectInfo, found := k.GetObjectInfo(ctx, req.BucketName, req.ObjectName)
-	if found {
-		return &types.QueryHeadObjectResponse{
-			ObjectInfo: objectInfo,
-		}, nil
+	objectInfo, objectFound := k.GetObjectInfo(ctx, req.BucketName, req.ObjectName)
+	if !objectFound {
+		return nil, types.ErrNoSuchObject
+	}
+
+	bucketInfo, found := k.GetBucketInfo(ctx, req.BucketName)
+	if !found {
+		return nil, types.ErrNoSuchBucket
+	}
+	var gvg *types2.GlobalVirtualGroup
+	if objectInfo.ObjectStatus == types.OBJECT_STATUS_SEALED {
+		gvgFound := false
+		gvg, gvgFound = k.GetObjectGVG(ctx, bucketInfo.Id, objectInfo.LocalVirtualGroupId)
+		if !gvgFound {
+			return nil, types.ErrInvalidGlobalVirtualGroup.Wrapf("gvg not found. objectInfo: %s", objectInfo.String())
+		}
 	}
-	return nil, types.ErrNoSuchObject
+	return &types.QueryHeadObjectResponse{
+		ObjectInfo:         objectInfo,
+		GlobalVirtualGroup: gvg,
+	}, nil
 }
 
 func (k Keeper) HeadObjectById(goCtx context.Context, req *types.QueryHeadObjectByIdRequest) (*types.QueryHeadObjectResponse, error) {
@@ -83,12 +99,27 @@ func (k Keeper) HeadObjectById(goCtx context.Context, req *types.QueryHeadObject
 	}
 
 	objectInfo, found := k.GetObjectInfoById(ctx, id)
-	if found {
-		return &types.QueryHeadObjectResponse{
-			ObjectInfo: objectInfo,
-		}, nil
+	if !found {
+		return nil, types.ErrNoSuchObject
 	}
-	return nil, types.ErrNoSuchObject
+
+	bucketInfo, found := k.GetBucketInfo(ctx, objectInfo.BucketName)
+	if !found {
+		return nil, types.ErrNoSuchBucket
+	}
+	var gvg *types2.GlobalVirtualGroup
+	if objectInfo.ObjectStatus == types.OBJECT_STATUS_SEALED {
+		gvgFound := false
+		gvg, gvgFound = k.GetObjectGVG(ctx, bucketInfo.Id, objectInfo.LocalVirtualGroupId)
+		if !gvgFound {
+			return nil, types.ErrInvalidGlobalVirtualGroup.Wrapf("gvg not found. objectInfo: %s", objectInfo.String())
+		}
+	}
+	return &types.QueryHeadObjectResponse{
+		ObjectInfo:         objectInfo,
+		GlobalVirtualGroup: gvg,
+	}, nil
+
 }
 
 func (k Keeper) ListBuckets(goCtx context.Context, req *types.QueryListBucketsRequest) (*types.QueryListBucketsResponse, error) {
@@ -133,7 +164,7 @@ func (k Keeper) ListObjects(goCtx context.Context, req *types.QueryListObjectsRe
 	objectPrefixStore := prefix.NewStore(store, types.GetObjectKeyOnlyBucketPrefix(req.BucketName))
 
 	pageRes, err := query.Paginate(objectPrefixStore, req.Pagination, func(key []byte, value []byte) error {
-		objectInfo, found := k.GetObjectInfoById(ctx, sequence.DecodeSequence(value))
+		objectInfo, found := k.GetObjectInfoById(ctx, k.objectSeq.DecodeSequence(value))
 		if found {
 			objectInfos = append(objectInfos, objectInfo)
 		}
@@ -169,7 +200,8 @@ func (k Keeper) ListObjectsByBucketId(goCtx context.Context, req *types.QueryLis
 	objectPrefixStore := prefix.NewStore(store, types.GetObjectKeyOnlyBucketPrefix(bucketInfo.BucketName))
 
 	pageRes, err := query.Paginate(objectPrefixStore, req.Pagination, func(key []byte, value []byte) error {
-		objectInfo, found := k.GetObjectInfoById(ctx, types.DecodeSequence(value))
+		u256Seq := sequence.Sequence[math.Uint]{}
+		objectInfo, found := k.GetObjectInfoById(ctx, u256Seq.DecodeSequence(value))
 		if found {
 			objectInfos = append(objectInfos, objectInfo)
 		}
@@ -306,7 +338,7 @@ func (k Keeper) VerifyPermission(goCtx context.Context, req *types.QueryVerifyPe
 	}
 
 	if req.BucketName == "" {
-		return nil, errors.Wrapf(types.ErrInvalidParameter, "No bucket specified")
+		return nil, errors.Wrapf(errors2.ErrInvalidParameter, "No bucket specified")
 	}
 
 	bucketInfo, found := k.GetBucketInfo(ctx, req.BucketName)
@@ -369,7 +401,7 @@ func (k Keeper) ListGroup(goCtx context.Context, req *types.QueryListGroupReques
 	groupStore := prefix.NewStore(store, types.GetGroupKeyOnlyOwnerPrefix(owner))
 
 	pageRes, err := query.Paginate(groupStore, req.Pagination, func(key []byte, value []byte) error {
-		groupInfo, found := k.GetGroupInfoById(ctx, sequence.DecodeSequence(value))
+		groupInfo, found := k.GetGroupInfoById(ctx, k.groupSeq.DecodeSequence(value))
 		if found {
 			groupInfos = append(groupInfos, groupInfo)
 		}
diff --git a/x/storage/keeper/virtualgroup.go b/x/storage/keeper/virtualgroup.go
new file mode 100644
index 000000000..06fc7ff0d
--- /dev/null
+++ b/x/storage/keeper/virtualgroup.go
@@ -0,0 +1,189 @@
+package keeper
+
+import (
+	"cosmossdk.io/math"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/prysmaticlabs/prysm/crypto/bls"
+
+	gnfdtypes "github.com/bnb-chain/greenfield/types"
+	"github.com/bnb-chain/greenfield/x/storage/types"
+	vgtypes "github.com/bnb-chain/greenfield/x/virtualgroup/types"
+)
+
+func (k Keeper) DeleteObjectFromVirtualGroup(ctx sdk.Context, bucketInfo *types.BucketInfo, objectInfo *types.ObjectInfo) error {
+
+	internalBucketInfo := k.MustGetInternalBucketInfo(ctx, bucketInfo.Id)
+
+	lvg := internalBucketInfo.MustGetLVG(objectInfo.LocalVirtualGroupId)
+
+	gvg, found := k.virtualGroupKeeper.GetGVG(ctx, lvg.GlobalVirtualGroupId)
+	if !found {
+		ctx.Logger().Error("GVG Not Exist, bucketID: %s, gvgID: %d, lvgID :%d", bucketInfo.Id.String(), lvg.GlobalVirtualGroupId, lvg.Id)
+		return vgtypes.ErrGVGNotExist
+	}
+
+	// TODO: if the store size is 0, remove it.
+	lvg.StoredSize -= objectInfo.PayloadSize
+	gvg.StoredSize -= objectInfo.PayloadSize
+
+	k.virtualGroupKeeper.SetGVG(ctx, gvg)
+	k.SetInternalBucketInfo(ctx, bucketInfo.Id, internalBucketInfo)
+	return nil
+}
+
+func (k Keeper) RebindingVirtualGroup(ctx sdk.Context, bucketInfo *types.BucketInfo, internalBucketInfo *types.InternalBucketInfo, gvgMappings []*types.GVGMapping) error {
+
+	gvgsMap := make(map[uint32]uint32)
+	for _, mapping := range gvgMappings {
+		gvgsMap[mapping.SrcGlobalVirtualGroupId] = mapping.DstGlobalVirtualGroupId
+	}
+
+	for _, lvg := range internalBucketInfo.LocalVirtualGroups {
+		dstGVGID, exist := gvgsMap[lvg.GlobalVirtualGroupId]
+		if !exist {
+			return types.ErrVirtualGroupOperateFailed.Wrapf("global virtual group not found in GVGMapping of  message. ID: %d", lvg.GlobalVirtualGroupId)
+		}
+
+		dstGVG, found := k.virtualGroupKeeper.GetGVG(ctx, dstGVGID)
+		if !found {
+			return types.ErrVirtualGroupOperateFailed.Wrapf("dst global virtual group not found in blockchain state. ID: %d", dstGVGID)
+		}
+
+		srcGVG, found := k.virtualGroupKeeper.GetGVG(ctx, lvg.GlobalVirtualGroupId)
+		if !found {
+			return types.ErrVirtualGroupOperateFailed.Wrapf("src global virtual group not found in blockchain state. ID: %d", lvg.GlobalVirtualGroupId)
+		}
+
+		err := k.virtualGroupKeeper.SettleAndDistributeGVG(ctx, srcGVG)
+		if err != nil {
+			return types.ErrVirtualGroupOperateFailed.Wrapf("fail to settle gvg. ID: %d", srcGVG.Id)
+		}
+
+		srcGVG.StoredSize -= lvg.StoredSize
+		dstGVG.StoredSize += lvg.StoredSize
+
+		lvg.GlobalVirtualGroupId = dstGVGID
+
+		k.virtualGroupKeeper.SetGVG(ctx, srcGVG)
+		k.virtualGroupKeeper.SetGVG(ctx, dstGVG)
+	}
+
+	err := k.ChargeBucketReadStoreFee(ctx, bucketInfo, internalBucketInfo)
+	if err != nil {
+		return types.ErrMigrationBucketFailed.Wrapf("charge bucket failed, err: %s", err)
+	}
+
+	k.SetInternalBucketInfo(ctx, bucketInfo.Id, internalBucketInfo)
+
+	return nil
+}
+
+func (k Keeper) VerifyGVGSecondarySPsBlsSignature(ctx sdk.Context, gvg *vgtypes.GlobalVirtualGroup, signHash [32]byte, signature []byte) error {
+	secondarySpBlsPubKeys := make([]bls.PublicKey, 0, len(gvg.SecondarySpIds))
+	for _, spId := range gvg.GetSecondarySpIds() {
+		secondarySp, found := k.spKeeper.GetStorageProvider(ctx, spId)
+		if !found {
+			panic("should not happen")
+		}
+		spBlsPubKey, err := bls.PublicKeyFromBytes(secondarySp.BlsKey)
+		if err != nil {
+			return types.ErrInvalidBlsPubKey.Wrapf("BLS public key converts failed: %v", err)
+		}
+		secondarySpBlsPubKeys = append(secondarySpBlsPubKeys, spBlsPubKey)
+	}
+	return gnfdtypes.VerifyBlsAggSignature(secondarySpBlsPubKeys, signHash, signature)
+}
+
+func (k Keeper) SealObjectOnVirtualGroup(ctx sdk.Context, bucketInfo *types.BucketInfo, gvgID uint32, objectInfo *types.ObjectInfo) (*types.LocalVirtualGroup, error) {
+	gvg, err := k.virtualGroupKeeper.GetGlobalVirtualGroupIfAvailable(ctx, gvgID, objectInfo.PayloadSize)
+	if err != nil {
+		return nil, err
+	}
+
+	internalBucketInfo := k.MustGetInternalBucketInfo(ctx, bucketInfo.Id)
+
+	lvg, found := internalBucketInfo.GetLVGByGVGID(gvgID)
+	if !found {
+		if len(internalBucketInfo.LocalVirtualGroups) > int(k.MaxLocalVirtualGroupNumPerBucket(ctx)) {
+			return nil, types.ErrVirtualGroupOperateFailed.Wrapf("The local virtual groups binding on bucket are exceed limitation. Num: %d, Allows: %d", len(internalBucketInfo.LocalVirtualGroups), k.MaxLocalVirtualGroupNumPerBucket(ctx))
+		}
+		// create a new lvg and add to the internalBucketInfo
+		lvg = &types.LocalVirtualGroup{
+			Id:                   internalBucketInfo.GetMaxLVGID() + 1,
+			GlobalVirtualGroupId: gvg.Id,
+			StoredSize:           objectInfo.PayloadSize,
+		}
+		internalBucketInfo.AppendLVG(lvg)
+	} else {
+		lvg.StoredSize += objectInfo.PayloadSize
+	}
+	gvg.StoredSize += objectInfo.PayloadSize
+
+	objectInfo.LocalVirtualGroupId = lvg.Id
+
+	if objectInfo.PayloadSize == 0 {
+		// unlock and charge store fee
+		err = k.ChargeObjectStoreFee(ctx, bucketInfo, internalBucketInfo, objectInfo)
+		if err != nil {
+			return nil, err
+		}
+	} else {
+		// unlock and charge store fee
+		err = k.UnlockAndChargeObjectStoreFee(ctx, bucketInfo, internalBucketInfo, objectInfo)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	k.virtualGroupKeeper.SetGVG(ctx, gvg)
+	k.SetInternalBucketInfo(ctx, bucketInfo.Id, internalBucketInfo)
+
+	if !found {
+		if err := ctx.EventManager().EmitTypedEvents(&vgtypes.EventCreateLocalVirtualGroup{
+			Id:                   lvg.Id,
+			BucketId:             bucketInfo.Id,
+			GlobalVirtualGroupId: lvg.GlobalVirtualGroupId,
+			StoredSize:           lvg.StoredSize,
+		}); err != nil {
+			return nil, err
+		}
+	} else {
+		if err := ctx.EventManager().EmitTypedEvents(&vgtypes.EventUpdateLocalVirtualGroup{
+			Id:                   lvg.Id,
+			GlobalVirtualGroupId: lvg.GlobalVirtualGroupId,
+			StoredSize:           lvg.StoredSize,
+		}); err != nil {
+			return nil, err
+		}
+	}
+
+	return lvg, nil
+}
+
+func (k Keeper) SealEmptyObjectOnVirtualGroup(ctx sdk.Context, bucketInfo *types.BucketInfo, objectInfo *types.ObjectInfo) (*types.LocalVirtualGroup, error) {
+	family, found := k.virtualGroupKeeper.GetGVGFamily(ctx, bucketInfo.PrimarySpId, bucketInfo.GlobalVirtualGroupFamilyId)
+	if !found {
+		return nil, vgtypes.ErrGVGFamilyNotExist
+	}
+
+	if len(family.GlobalVirtualGroupIds) == 0 {
+		return nil, vgtypes.ErrGVGNotExist.Wrapf("The gvg family has no gvg")
+	}
+
+	// use the first gvg by default.
+	gvgID := family.GlobalVirtualGroupIds[0]
+
+	return k.SealObjectOnVirtualGroup(ctx, bucketInfo, gvgID, objectInfo)
+}
+
+func (k Keeper) GetObjectGVG(ctx sdk.Context, bucketID math.Uint, lvgID uint32) (*vgtypes.GlobalVirtualGroup, bool) {
+	internalBucketInfo := k.MustGetInternalBucketInfo(ctx, bucketID)
+
+	lvg, found := internalBucketInfo.GetLVG(lvgID)
+	if !found {
+		return nil, false
+	}
+
+	return k.virtualGroupKeeper.GetGVG(ctx, lvg.GlobalVirtualGroupId)
+
+}
diff --git a/x/storage/module_simulation.go b/x/storage/module_simulation.go
index 47fd8ea67..2262f412b 100644
--- a/x/storage/module_simulation.go
+++ b/x/storage/module_simulation.go
@@ -48,6 +48,7 @@ func (am AppModule) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) {}
 // WeightedOperations returns the all the gov module operations with their respective weights.
 func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation {
 	operations := make([]simtypes.WeightedOperation, 0)
+
 	// this line is used by starport scaffolding # simapp/module/operation
 	return operations
 }
diff --git a/x/storage/simulation/cancel_migrate_bucket.go b/x/storage/simulation/cancel_migrate_bucket.go
new file mode 100644
index 000000000..27225d60a
--- /dev/null
+++ b/x/storage/simulation/cancel_migrate_bucket.go
@@ -0,0 +1,30 @@
+package simulation
+
+import (
+	"math/rand"
+
+	"github.com/cosmos/cosmos-sdk/baseapp"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
+
+	"github.com/bnb-chain/greenfield/x/storage/keeper"
+	"github.com/bnb-chain/greenfield/x/storage/types"
+)
+
+func SimulateMsgCancelMigrateBucket(
+	ak types.AccountKeeper,
+	bk types.BankKeeper,
+	k keeper.Keeper,
+) simtypes.Operation {
+	return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
+	) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
+		simAccount, _ := simtypes.RandomAcc(r, accs)
+		msg := &types.MsgCancelMigrateBucket{
+			Operator: simAccount.Address.String(),
+		}
+
+		// TODO: Handling the CancelMigrateBucket simulation
+
+		return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "CancelMigrateBucket simulation not implemented"), nil, nil
+	}
+}
diff --git a/x/storage/simulation/complete_migrate_bucket.go b/x/storage/simulation/complete_migrate_bucket.go
new file mode 100644
index 000000000..17bb5a80b
--- /dev/null
+++ b/x/storage/simulation/complete_migrate_bucket.go
@@ -0,0 +1,30 @@
+package simulation
+
+import (
+	"math/rand"
+
+	"github.com/cosmos/cosmos-sdk/baseapp"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
+
+	"github.com/bnb-chain/greenfield/x/storage/keeper"
+	"github.com/bnb-chain/greenfield/x/storage/types"
+)
+
+func SimulateMsgCompleteMigrateBucket(
+	ak types.AccountKeeper,
+	bk types.BankKeeper,
+	k keeper.Keeper,
+) simtypes.Operation {
+	return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
+	) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
+		simAccount, _ := simtypes.RandomAcc(r, accs)
+		msg := &types.MsgCompleteMigrateBucket{
+			Operator: simAccount.Address.String(),
+		}
+
+		// TODO: Handling the CompleteMigrateBucket simulation
+
+		return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "CompleteMigrateBucket simulation not implemented"), nil, nil
+	}
+}
diff --git a/x/storage/simulation/migrate_bucket.go b/x/storage/simulation/migrate_bucket.go
new file mode 100644
index 000000000..203ac1782
--- /dev/null
+++ b/x/storage/simulation/migrate_bucket.go
@@ -0,0 +1,30 @@
+package simulation
+
+import (
+	"math/rand"
+
+	"github.com/cosmos/cosmos-sdk/baseapp"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
+
+	"github.com/bnb-chain/greenfield/x/storage/keeper"
+	"github.com/bnb-chain/greenfield/x/storage/types"
+)
+
+func SimulateMsgMigrateBucket(
+	ak types.AccountKeeper,
+	bk types.BankKeeper,
+	k keeper.Keeper,
+) simtypes.Operation {
+	return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
+	) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
+		simAccount, _ := simtypes.RandomAcc(r, accs)
+		msg := &types.MsgMigrateBucket{
+			Operator: simAccount.Address.String(),
+		}
+
+		// TODO: Handling the MigrateBucket simulation
+
+		return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "MigrateBucket simulation not implemented"), nil, nil
+	}
+}
diff --git a/x/storage/types/codec.go b/x/storage/types/codec.go
index dd987e18e..0efba3945 100644
--- a/x/storage/types/codec.go
+++ b/x/storage/types/codec.go
@@ -23,6 +23,9 @@ func RegisterCodec(cdc *codec.LegacyAmino) {
 	cdc.RegisterConcrete(&MsgUpdateBucketInfo{}, "storage/UpdateBucketInfo", nil)
 	cdc.RegisterConcrete(&MsgCancelCreateObject{}, "storage/CancelCreateObject", nil)
 	cdc.RegisterConcrete(&MsgDeletePolicy{}, "storage/DeletePolicy", nil)
+	cdc.RegisterConcrete(&MsgMigrateBucket{}, "storage/MigrateBucket", nil)
+	cdc.RegisterConcrete(&MsgCompleteMigrateBucket{}, "storage/CompleteMigrateBucket", nil)
+	cdc.RegisterConcrete(&MsgCancelMigrateBucket{}, "storage/CancelMigrateBucket", nil)
 	// this line is used by starport scaffolding # 2
 }
 
@@ -100,6 +103,15 @@ func RegisterInterfaces(registry cdctypes.InterfaceRegistry) {
 	registry.RegisterImplementations((*sdk.Msg)(nil),
 		&MsgUpdateParams{},
 	)
+	registry.RegisterImplementations((*sdk.Msg)(nil),
+		&MsgMigrateBucket{},
+	)
+	registry.RegisterImplementations((*sdk.Msg)(nil),
+		&MsgCompleteMigrateBucket{},
+	)
+	registry.RegisterImplementations((*sdk.Msg)(nil),
+		&MsgCancelMigrateBucket{},
+	)
 	// this line is used by starport scaffolding # 3
 
 	msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
diff --git a/x/storage/types/common.go b/x/storage/types/common.go
index dac81ff82..ec830f78c 100644
--- a/x/storage/types/common.go
+++ b/x/storage/types/common.go
@@ -1,19 +1,54 @@
 package types
 
 import (
+	"bytes"
+	"crypto/sha256"
+
 	"cosmossdk.io/math"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 )
 
-func NewSecondarySpSignDoc(spAddress sdk.AccAddress, objectID math.Uint, checksum []byte) *SecondarySpSignDoc {
-	return &SecondarySpSignDoc{
-		SpAddress: spAddress.String(),
-		ObjectId:  objectID,
-		Checksum:  checksum,
+// NewSecondarySpSealObjectSignDoc creates the doc for all secondary sps bls signings,
+// checksums is the hash of slice of integrity hash(objectInfo checkSums) contributed by secondary sps
+func NewSecondarySpSealObjectSignDoc(chainID string, gvgID uint32, objectID math.Uint, checksum []byte) *SecondarySpSealObjectSignDoc {
+	return &SecondarySpSealObjectSignDoc{
+		ChainId:              chainID,
+		GlobalVirtualGroupId: gvgID,
+		ObjectId:             objectID,
+		Checksum:             checksum,
+	}
+}
+
+func (c *SecondarySpSealObjectSignDoc) GetSignBytes() []byte {
+	return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(c))
+}
+
+func (c *SecondarySpSealObjectSignDoc) GetBlsSignHash() [32]byte {
+	return sdk.Keccak256Hash(c.GetSignBytes())
+}
+
+// GenerateHash generates sha256 hash of checksums from objectInfo
+func GenerateHash(checksumList [][]byte) []byte {
+	hash := sha256.New()
+	checksumBytesTotal := bytes.Join(checksumList, []byte(""))
+	hash.Write(checksumBytesTotal)
+	return hash.Sum(nil)
+}
+
+func NewSecondarySpMigrationBucketSignDoc(chainID string, bucketID math.Uint, spID, srcGVGID, dstGVGID uint32) *SecondarySpMigrationBucketSignDoc {
+	return &SecondarySpMigrationBucketSignDoc{
+		ChainId:                 chainID,
+		BucketId:                bucketID,
+		DstPrimarySpId:          spID,
+		SrcGlobalVirtualGroupId: srcGVGID,
+		DstGlobalVirtualGroupId: dstGVGID,
 	}
 }
 
-func (sr *SecondarySpSignDoc) GetSignBytes() []byte {
-	bz := ModuleCdc.MustMarshalJSON(sr)
-	return sdk.MustSortJSON(bz)
+func (c *SecondarySpMigrationBucketSignDoc) GetSignBytes() []byte {
+	return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(c))
+}
+
+func (c *SecondarySpMigrationBucketSignDoc) GetBlsSignHash() [32]byte {
+	return sdk.Keccak256Hash(c.GetSignBytes())
 }
diff --git a/x/storage/types/common.pb.go b/x/storage/types/common.pb.go
index b352f3f1d..bb90a208a 100644
--- a/x/storage/types/common.pb.go
+++ b/x/storage/types/common.pb.go
@@ -62,16 +62,19 @@ type BucketStatus int32
 const (
 	BUCKET_STATUS_CREATED      BucketStatus = 0
 	BUCKET_STATUS_DISCONTINUED BucketStatus = 1
+	BUCKET_STATUS_MIGRATING    BucketStatus = 2
 )
 
 var BucketStatus_name = map[int32]string{
 	0: "BUCKET_STATUS_CREATED",
 	1: "BUCKET_STATUS_DISCONTINUED",
+	2: "BUCKET_STATUS_MIGRATING",
 }
 
 var BucketStatus_value = map[string]int32{
 	"BUCKET_STATUS_CREATED":      0,
 	"BUCKET_STATUS_DISCONTINUED": 1,
+	"BUCKET_STATUS_MIGRATING":    2,
 }
 
 func (x BucketStatus) String() string {
@@ -175,28 +178,31 @@ func (VisibilityType) EnumDescriptor() ([]byte, []int) {
 	return fileDescriptor_4eff6c0fa4aaf4c9, []int{4}
 }
 
-// Approval is the signature information returned by the Primary Storage Provider (SP) to the user
-// after allowing them to create a bucket or object, which is then used for verification on the chain
-// to ensure agreement between the Primary SP and the user."
-type Approval struct {
-	// expired_height is the block height at which the signature expires.
-	ExpiredHeight uint64 `protobuf:"varint,1,opt,name=expired_height,json=expiredHeight,proto3" json:"expired_height,omitempty"`
-	// The signature needs to conform to the EIP 712 specification.
-	Sig []byte `protobuf:"bytes,2,opt,name=sig,proto3" json:"sig,omitempty"`
+// SecondarySpSealObjectSignDoc used to generate seal signature of secondary SP
+// If the secondary SP only signs the checksum to declare the object pieces are saved,
+// it might be reused by the primary SP to fake it's declaration.
+// Then the primary SP can challenge and slash the secondary SP.
+// So the id of the object is needed to prevent this.
+type SecondarySpSealObjectSignDoc struct {
+	ChainId              string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"`
+	GlobalVirtualGroupId uint32 `protobuf:"varint,2,opt,name=global_virtual_group_id,json=globalVirtualGroupId,proto3" json:"global_virtual_group_id,omitempty"`
+	ObjectId             Uint   `protobuf:"bytes,3,opt,name=object_id,json=objectId,proto3,customtype=Uint" json:"object_id"`
+	// checksum is the sha256 hash of slice of integrity hash from secondary sps
+	Checksum []byte `protobuf:"bytes,4,opt,name=checksum,proto3" json:"checksum,omitempty"`
 }
 
-func (m *Approval) Reset()         { *m = Approval{} }
-func (m *Approval) String() string { return proto.CompactTextString(m) }
-func (*Approval) ProtoMessage()    {}
-func (*Approval) Descriptor() ([]byte, []int) {
+func (m *SecondarySpSealObjectSignDoc) Reset()         { *m = SecondarySpSealObjectSignDoc{} }
+func (m *SecondarySpSealObjectSignDoc) String() string { return proto.CompactTextString(m) }
+func (*SecondarySpSealObjectSignDoc) ProtoMessage()    {}
+func (*SecondarySpSealObjectSignDoc) Descriptor() ([]byte, []int) {
 	return fileDescriptor_4eff6c0fa4aaf4c9, []int{0}
 }
-func (m *Approval) XXX_Unmarshal(b []byte) error {
+func (m *SecondarySpSealObjectSignDoc) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
 }
-func (m *Approval) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+func (m *SecondarySpSealObjectSignDoc) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
 	if deterministic {
-		return xxx_messageInfo_Approval.Marshal(b, m, deterministic)
+		return xxx_messageInfo_SecondarySpSealObjectSignDoc.Marshal(b, m, deterministic)
 	} else {
 		b = b[:cap(b)]
 		n, err := m.MarshalToSizedBuffer(b)
@@ -206,55 +212,57 @@ func (m *Approval) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
 		return b[:n], nil
 	}
 }
-func (m *Approval) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Approval.Merge(m, src)
+func (m *SecondarySpSealObjectSignDoc) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_SecondarySpSealObjectSignDoc.Merge(m, src)
 }
-func (m *Approval) XXX_Size() int {
+func (m *SecondarySpSealObjectSignDoc) XXX_Size() int {
 	return m.Size()
 }
-func (m *Approval) XXX_DiscardUnknown() {
-	xxx_messageInfo_Approval.DiscardUnknown(m)
+func (m *SecondarySpSealObjectSignDoc) XXX_DiscardUnknown() {
+	xxx_messageInfo_SecondarySpSealObjectSignDoc.DiscardUnknown(m)
 }
 
-var xxx_messageInfo_Approval proto.InternalMessageInfo
+var xxx_messageInfo_SecondarySpSealObjectSignDoc proto.InternalMessageInfo
+
+func (m *SecondarySpSealObjectSignDoc) GetChainId() string {
+	if m != nil {
+		return m.ChainId
+	}
+	return ""
+}
 
-func (m *Approval) GetExpiredHeight() uint64 {
+func (m *SecondarySpSealObjectSignDoc) GetGlobalVirtualGroupId() uint32 {
 	if m != nil {
-		return m.ExpiredHeight
+		return m.GlobalVirtualGroupId
 	}
 	return 0
 }
 
-func (m *Approval) GetSig() []byte {
+func (m *SecondarySpSealObjectSignDoc) GetChecksum() []byte {
 	if m != nil {
-		return m.Sig
+		return m.Checksum
 	}
 	return nil
 }
 
-// SecondarySpSignDoc used to generate seal signature of secondary SP
-// If the secondary SP only signs the checksum to declare the object pieces are saved,
-// it might be reused by the primary SP to fake it's declaration.
-// Then the primary SP can challenge and slash the secondary SP.
-// So the id of the object is needed to prevent this.
-type SecondarySpSignDoc struct {
-	SpAddress string `protobuf:"bytes,1,opt,name=sp_address,json=spAddress,proto3" json:"sp_address,omitempty"`
-	ObjectId  Uint   `protobuf:"bytes,2,opt,name=object_id,json=objectId,proto3,customtype=Uint" json:"object_id"`
-	Checksum  []byte `protobuf:"bytes,3,opt,name=checksum,proto3" json:"checksum,omitempty"`
+type GVGMapping struct {
+	SrcGlobalVirtualGroupId uint32 `protobuf:"varint,1,opt,name=src_global_virtual_group_id,json=srcGlobalVirtualGroupId,proto3" json:"src_global_virtual_group_id,omitempty"`
+	DstGlobalVirtualGroupId uint32 `protobuf:"varint,2,opt,name=dst_global_virtual_group_id,json=dstGlobalVirtualGroupId,proto3" json:"dst_global_virtual_group_id,omitempty"`
+	SecondarySpBlsSignature []byte `protobuf:"bytes,3,opt,name=secondary_sp_bls_signature,json=secondarySpBlsSignature,proto3" json:"secondary_sp_bls_signature,omitempty"`
 }
 
-func (m *SecondarySpSignDoc) Reset()         { *m = SecondarySpSignDoc{} }
-func (m *SecondarySpSignDoc) String() string { return proto.CompactTextString(m) }
-func (*SecondarySpSignDoc) ProtoMessage()    {}
-func (*SecondarySpSignDoc) Descriptor() ([]byte, []int) {
+func (m *GVGMapping) Reset()         { *m = GVGMapping{} }
+func (m *GVGMapping) String() string { return proto.CompactTextString(m) }
+func (*GVGMapping) ProtoMessage()    {}
+func (*GVGMapping) Descriptor() ([]byte, []int) {
 	return fileDescriptor_4eff6c0fa4aaf4c9, []int{1}
 }
-func (m *SecondarySpSignDoc) XXX_Unmarshal(b []byte) error {
+func (m *GVGMapping) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
 }
-func (m *SecondarySpSignDoc) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+func (m *GVGMapping) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
 	if deterministic {
-		return xxx_messageInfo_SecondarySpSignDoc.Marshal(b, m, deterministic)
+		return xxx_messageInfo_GVGMapping.Marshal(b, m, deterministic)
 	} else {
 		b = b[:cap(b)]
 		n, err := m.MarshalToSizedBuffer(b)
@@ -264,88 +272,255 @@ func (m *SecondarySpSignDoc) XXX_Marshal(b []byte, deterministic bool) ([]byte,
 		return b[:n], nil
 	}
 }
-func (m *SecondarySpSignDoc) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_SecondarySpSignDoc.Merge(m, src)
+func (m *GVGMapping) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_GVGMapping.Merge(m, src)
 }
-func (m *SecondarySpSignDoc) XXX_Size() int {
+func (m *GVGMapping) XXX_Size() int {
 	return m.Size()
 }
-func (m *SecondarySpSignDoc) XXX_DiscardUnknown() {
-	xxx_messageInfo_SecondarySpSignDoc.DiscardUnknown(m)
+func (m *GVGMapping) XXX_DiscardUnknown() {
+	xxx_messageInfo_GVGMapping.DiscardUnknown(m)
 }
 
-var xxx_messageInfo_SecondarySpSignDoc proto.InternalMessageInfo
+var xxx_messageInfo_GVGMapping proto.InternalMessageInfo
 
-func (m *SecondarySpSignDoc) GetSpAddress() string {
+func (m *GVGMapping) GetSrcGlobalVirtualGroupId() uint32 {
 	if m != nil {
-		return m.SpAddress
+		return m.SrcGlobalVirtualGroupId
 	}
-	return ""
+	return 0
 }
 
-func (m *SecondarySpSignDoc) GetChecksum() []byte {
+func (m *GVGMapping) GetDstGlobalVirtualGroupId() uint32 {
 	if m != nil {
-		return m.Checksum
+		return m.DstGlobalVirtualGroupId
+	}
+	return 0
+}
+
+func (m *GVGMapping) GetSecondarySpBlsSignature() []byte {
+	if m != nil {
+		return m.SecondarySpBlsSignature
 	}
 	return nil
 }
 
+type SecondarySpMigrationBucketSignDoc struct {
+	ChainId                 string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"`
+	DstPrimarySpId          uint32 `protobuf:"varint,2,opt,name=dst_primary_sp_id,json=dstPrimarySpId,proto3" json:"dst_primary_sp_id,omitempty"`
+	SrcGlobalVirtualGroupId uint32 `protobuf:"varint,3,opt,name=src_global_virtual_group_id,json=srcGlobalVirtualGroupId,proto3" json:"src_global_virtual_group_id,omitempty"`
+	DstGlobalVirtualGroupId uint32 `protobuf:"varint,4,opt,name=dst_global_virtual_group_id,json=dstGlobalVirtualGroupId,proto3" json:"dst_global_virtual_group_id,omitempty"`
+	BucketId                Uint   `protobuf:"bytes,5,opt,name=bucket_id,json=bucketId,proto3,customtype=Uint" json:"bucket_id"`
+}
+
+func (m *SecondarySpMigrationBucketSignDoc) Reset()         { *m = SecondarySpMigrationBucketSignDoc{} }
+func (m *SecondarySpMigrationBucketSignDoc) String() string { return proto.CompactTextString(m) }
+func (*SecondarySpMigrationBucketSignDoc) ProtoMessage()    {}
+func (*SecondarySpMigrationBucketSignDoc) Descriptor() ([]byte, []int) {
+	return fileDescriptor_4eff6c0fa4aaf4c9, []int{2}
+}
+func (m *SecondarySpMigrationBucketSignDoc) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *SecondarySpMigrationBucketSignDoc) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_SecondarySpMigrationBucketSignDoc.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *SecondarySpMigrationBucketSignDoc) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_SecondarySpMigrationBucketSignDoc.Merge(m, src)
+}
+func (m *SecondarySpMigrationBucketSignDoc) XXX_Size() int {
+	return m.Size()
+}
+func (m *SecondarySpMigrationBucketSignDoc) XXX_DiscardUnknown() {
+	xxx_messageInfo_SecondarySpMigrationBucketSignDoc.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_SecondarySpMigrationBucketSignDoc proto.InternalMessageInfo
+
+func (m *SecondarySpMigrationBucketSignDoc) GetChainId() string {
+	if m != nil {
+		return m.ChainId
+	}
+	return ""
+}
+
+func (m *SecondarySpMigrationBucketSignDoc) GetDstPrimarySpId() uint32 {
+	if m != nil {
+		return m.DstPrimarySpId
+	}
+	return 0
+}
+
+func (m *SecondarySpMigrationBucketSignDoc) GetSrcGlobalVirtualGroupId() uint32 {
+	if m != nil {
+		return m.SrcGlobalVirtualGroupId
+	}
+	return 0
+}
+
+func (m *SecondarySpMigrationBucketSignDoc) GetDstGlobalVirtualGroupId() uint32 {
+	if m != nil {
+		return m.DstGlobalVirtualGroupId
+	}
+	return 0
+}
+
+// Local virtual group(LVG) uniquely associated with a global virtual group.
+// Each bucket maintains a mapping from local virtual group to global virtual group
+// Each local virtual group is associated with a unique virtual payment account,
+// where all object fees are streamed to.
+type LocalVirtualGroup struct {
+	// id is the identifier of the local virtual group.
+	Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
+	// global_virtual_group_id is the identifier of the global virtual group.
+	GlobalVirtualGroupId uint32 `protobuf:"varint,3,opt,name=global_virtual_group_id,json=globalVirtualGroupId,proto3" json:"global_virtual_group_id,omitempty"`
+	// stored_size is the size of the stored data in the local virtual group.
+	StoredSize uint64 `protobuf:"varint,4,opt,name=stored_size,json=storedSize,proto3" json:"stored_size,omitempty"`
+	// total_charge_size is the total charged size of the objects in the LVG.
+	// Notice that the minimum unit of charge is 128K
+	TotalChargeSize uint64 `protobuf:"varint,5,opt,name=total_charge_size,json=totalChargeSize,proto3" json:"total_charge_size,omitempty"`
+}
+
+func (m *LocalVirtualGroup) Reset()         { *m = LocalVirtualGroup{} }
+func (m *LocalVirtualGroup) String() string { return proto.CompactTextString(m) }
+func (*LocalVirtualGroup) ProtoMessage()    {}
+func (*LocalVirtualGroup) Descriptor() ([]byte, []int) {
+	return fileDescriptor_4eff6c0fa4aaf4c9, []int{3}
+}
+func (m *LocalVirtualGroup) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *LocalVirtualGroup) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_LocalVirtualGroup.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *LocalVirtualGroup) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_LocalVirtualGroup.Merge(m, src)
+}
+func (m *LocalVirtualGroup) XXX_Size() int {
+	return m.Size()
+}
+func (m *LocalVirtualGroup) XXX_DiscardUnknown() {
+	xxx_messageInfo_LocalVirtualGroup.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_LocalVirtualGroup proto.InternalMessageInfo
+
+func (m *LocalVirtualGroup) GetId() uint32 {
+	if m != nil {
+		return m.Id
+	}
+	return 0
+}
+
+func (m *LocalVirtualGroup) GetGlobalVirtualGroupId() uint32 {
+	if m != nil {
+		return m.GlobalVirtualGroupId
+	}
+	return 0
+}
+
+func (m *LocalVirtualGroup) GetStoredSize() uint64 {
+	if m != nil {
+		return m.StoredSize
+	}
+	return 0
+}
+
+func (m *LocalVirtualGroup) GetTotalChargeSize() uint64 {
+	if m != nil {
+		return m.TotalChargeSize
+	}
+	return 0
+}
+
 func init() {
 	proto.RegisterEnum("greenfield.storage.SourceType", SourceType_name, SourceType_value)
 	proto.RegisterEnum("greenfield.storage.BucketStatus", BucketStatus_name, BucketStatus_value)
 	proto.RegisterEnum("greenfield.storage.RedundancyType", RedundancyType_name, RedundancyType_value)
 	proto.RegisterEnum("greenfield.storage.ObjectStatus", ObjectStatus_name, ObjectStatus_value)
 	proto.RegisterEnum("greenfield.storage.VisibilityType", VisibilityType_name, VisibilityType_value)
-	proto.RegisterType((*Approval)(nil), "greenfield.storage.Approval")
-	proto.RegisterType((*SecondarySpSignDoc)(nil), "greenfield.storage.SecondarySpSignDoc")
+	proto.RegisterType((*SecondarySpSealObjectSignDoc)(nil), "greenfield.storage.SecondarySpSealObjectSignDoc")
+	proto.RegisterType((*GVGMapping)(nil), "greenfield.storage.GVGMapping")
+	proto.RegisterType((*SecondarySpMigrationBucketSignDoc)(nil), "greenfield.storage.SecondarySpMigrationBucketSignDoc")
+	proto.RegisterType((*LocalVirtualGroup)(nil), "greenfield.storage.LocalVirtualGroup")
 }
 
 func init() { proto.RegisterFile("greenfield/storage/common.proto", fileDescriptor_4eff6c0fa4aaf4c9) }
 
 var fileDescriptor_4eff6c0fa4aaf4c9 = []byte{
-	// 610 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x93, 0xc1, 0x4a, 0xdc, 0x40,
-	0x18, 0xc7, 0x13, 0x95, 0xe2, 0x4e, 0xad, 0x84, 0xc1, 0xd6, 0x75, 0x17, 0xb2, 0x22, 0x14, 0x44,
-	0xd0, 0x3d, 0xf4, 0xd0, 0x5e, 0x93, 0xc9, 0x54, 0xa7, 0x6e, 0x93, 0x65, 0x26, 0x11, 0xec, 0x25,
-	0x64, 0x93, 0x69, 0x76, 0xaa, 0x9b, 0x09, 0x49, 0xb6, 0xb8, 0x6f, 0xd0, 0x63, 0xe9, 0x2b, 0xf8,
-	0x0a, 0x3e, 0x84, 0x47, 0xf1, 0x54, 0x7a, 0x90, 0xa2, 0x2f, 0x52, 0x92, 0xac, 0xba, 0x4b, 0xf7,
-	0x36, 0xdf, 0xff, 0xff, 0xe7, 0xfb, 0x7e, 0xf3, 0xc1, 0x07, 0x3a, 0x71, 0xc6, 0x79, 0xf2, 0x55,
-	0xf0, 0xf3, 0xa8, 0x9b, 0x17, 0x32, 0x0b, 0x62, 0xde, 0x0d, 0xe5, 0x68, 0x24, 0x93, 0x83, 0x34,
-	0x93, 0x85, 0x84, 0xf0, 0x39, 0x70, 0x30, 0x0d, 0xb4, 0xb6, 0x42, 0x99, 0x8f, 0x64, 0xee, 0x57,
-	0x89, 0x6e, 0x5d, 0xd4, 0xf1, 0xd6, 0x46, 0x2c, 0x63, 0x59, 0xeb, 0xe5, 0xab, 0x56, 0x77, 0x10,
-	0x58, 0x35, 0xd2, 0x34, 0x93, 0xdf, 0x83, 0x73, 0xf8, 0x16, 0xac, 0xf3, 0x8b, 0x54, 0x64, 0x3c,
-	0xf2, 0x87, 0x5c, 0xc4, 0xc3, 0xa2, 0xa9, 0x6e, 0xab, 0xbb, 0x2b, 0xf4, 0xd5, 0x54, 0x3d, 0xaa,
-	0x44, 0xa8, 0x81, 0xe5, 0x5c, 0xc4, 0xcd, 0xa5, 0x6d, 0x75, 0x77, 0x8d, 0x96, 0xcf, 0x9d, 0x4b,
-	0x15, 0x40, 0xc6, 0x43, 0x99, 0x44, 0x41, 0x36, 0x61, 0x29, 0x13, 0x71, 0x62, 0xc9, 0x10, 0xbe,
-	0x07, 0x20, 0x4f, 0xfd, 0x20, 0x8a, 0x32, 0x9e, 0xe7, 0x55, 0xaf, 0x86, 0xd9, 0xbc, 0xbd, 0xda,
-	0xdf, 0x98, 0x72, 0x19, 0xb5, 0xc3, 0x8a, 0x4c, 0x24, 0x31, 0x6d, 0xe4, 0xe9, 0x54, 0x80, 0x1f,
-	0x40, 0x43, 0x0e, 0xbe, 0xf1, 0xb0, 0xf0, 0x45, 0x54, 0xcd, 0x69, 0x98, 0xed, 0xeb, 0xbb, 0x8e,
-	0xf2, 0xe7, 0xae, 0xb3, 0xe2, 0x89, 0xa4, 0xb8, 0xbd, 0xda, 0x7f, 0x39, 0xed, 0x51, 0x96, 0x74,
-	0xb5, 0x4e, 0x93, 0x08, 0xb6, 0xc0, 0x6a, 0x38, 0xe4, 0xe1, 0x59, 0x3e, 0x1e, 0x35, 0x97, 0x2b,
-	0xc0, 0xa7, 0x7a, 0xef, 0x0c, 0x00, 0x26, 0xc7, 0x59, 0xc8, 0xdd, 0x49, 0xca, 0xe1, 0x1b, 0x00,
-	0x99, 0xe3, 0x51, 0x84, 0x7d, 0xf7, 0xb4, 0x8f, 0x7d, 0x87, 0x92, 0x43, 0x62, 0x6b, 0x0a, 0xec,
-	0x80, 0xf6, 0xac, 0x6e, 0x32, 0xe4, 0x23, 0xea, 0x30, 0xe6, 0xa3, 0x23, 0x83, 0xd8, 0x9a, 0x0a,
-	0x75, 0xd0, 0x9a, 0x0d, 0x7c, 0x26, 0x94, 0x3a, 0xd4, 0xef, 0x63, 0xdb, 0x22, 0xf6, 0xa1, 0xb6,
-	0xd4, 0x5a, 0xf9, 0x71, 0xa9, 0x2b, 0x7b, 0x0e, 0x58, 0x33, 0xc7, 0xe1, 0x19, 0x2f, 0x58, 0x11,
-	0x14, 0xe3, 0x1c, 0x6e, 0x81, 0xd7, 0xa6, 0x87, 0x8e, 0xb1, 0xeb, 0x33, 0xd7, 0x70, 0x3d, 0xe6,
-	0x23, 0x8a, 0x0d, 0x17, 0x5b, 0x9a, 0x52, 0x36, 0x9c, 0xb7, 0x2c, 0xc2, 0x90, 0x63, 0xbb, 0xc4,
-	0xf6, 0xb0, 0xa5, 0xa9, 0xd3, 0x86, 0xc7, 0x60, 0x9d, 0xf2, 0x68, 0x9c, 0x44, 0x41, 0x12, 0x4e,
-	0x1e, 0x7f, 0x40, 0xb1, 0xe5, 0xd9, 0x96, 0x61, 0xa3, 0x53, 0x1f, 0xa3, 0x8a, 0x47, 0x53, 0x60,
-	0x1b, 0x6c, 0xce, 0xe8, 0x14, 0xf7, 0x7b, 0x04, 0x19, 0xb5, 0xf9, 0xd8, 0x4c, 0x80, 0x35, 0xa7,
-	0x5a, 0xd9, 0x33, 0x9d, 0x63, 0x7e, 0xc2, 0x68, 0x01, 0x5d, 0x13, 0x6c, 0xcc, 0x5b, 0x0c, 0x1b,
-	0xbd, 0x92, 0xab, 0xe4, 0x9e, 0x77, 0xe6, 0xb8, 0x1f, 0x17, 0xf1, 0x4b, 0x05, 0xeb, 0x27, 0x22,
-	0x17, 0x03, 0x71, 0x2e, 0x8a, 0x1a, 0xbc, 0x03, 0xda, 0x27, 0x84, 0x11, 0x93, 0xf4, 0x88, 0x7b,
-	0x5a, 0x6f, 0xd1, 0xb3, 0x59, 0x1f, 0x23, 0xf2, 0x91, 0x54, 0x33, 0x17, 0x04, 0xfa, 0x9e, 0xd9,
-	0x23, 0xc8, 0xa7, 0xd8, 0x28, 0x47, 0xb7, 0xc1, 0xe6, 0x7f, 0x01, 0x4a, 0x4e, 0x0c, 0x17, 0x6b,
-	0x4b, 0x8b, 0x4c, 0x62, 0x1f, 0x61, 0x4a, 0x5c, 0x6d, 0xb9, 0x86, 0x32, 0xc9, 0xf5, 0xbd, 0xae,
-	0xde, 0xdc, 0xeb, 0xea, 0xdf, 0x7b, 0x5d, 0xfd, 0xf9, 0xa0, 0x2b, 0x37, 0x0f, 0xba, 0xf2, 0xfb,
-	0x41, 0x57, 0xbe, 0x74, 0x63, 0x51, 0x0c, 0xc7, 0x83, 0x83, 0x50, 0x8e, 0xba, 0x83, 0x64, 0xb0,
-	0x1f, 0x0e, 0x03, 0x91, 0x74, 0x67, 0x4e, 0xf1, 0xe2, 0xe9, 0x18, 0x8b, 0x49, 0xca, 0xf3, 0xc1,
-	0x8b, 0xea, 0x8e, 0xde, 0xfd, 0x0b, 0x00, 0x00, 0xff, 0xff, 0x4e, 0xd4, 0x1c, 0xd6, 0xaf, 0x03,
-	0x00, 0x00,
-}
-
-func (m *Approval) Marshal() (dAtA []byte, err error) {
+	// 815 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x95, 0x4f, 0x6f, 0xe3, 0x44,
+	0x18, 0xc6, 0xe3, 0x34, 0x0b, 0xdb, 0xd9, 0x52, 0x52, 0xab, 0x90, 0x36, 0x41, 0x4e, 0xe9, 0xa9,
+	0x54, 0xda, 0xe6, 0x80, 0x90, 0x56, 0x62, 0x2f, 0xb6, 0x33, 0x64, 0x87, 0x4d, 0x9d, 0x68, 0xc6,
+	0x89, 0x54, 0x2e, 0x23, 0xff, 0x19, 0xdc, 0xa1, 0x8e, 0xc7, 0xf2, 0x8c, 0x11, 0xdd, 0x4f, 0xc0,
+	0x11, 0xf1, 0x05, 0x38, 0x70, 0xe0, 0x0b, 0xf0, 0x15, 0x40, 0x7b, 0x5c, 0x71, 0x42, 0x1c, 0x56,
+	0xa8, 0xfd, 0x22, 0xc8, 0x76, 0x92, 0x4d, 0x44, 0x88, 0x56, 0xe2, 0x96, 0x79, 0x9f, 0x5f, 0xde,
+	0xf7, 0x79, 0xdf, 0x79, 0x6d, 0x83, 0x6e, 0x94, 0x31, 0x96, 0x7c, 0xcd, 0x59, 0x1c, 0xf6, 0xa4,
+	0x12, 0x99, 0x17, 0xb1, 0x5e, 0x20, 0x66, 0x33, 0x91, 0x5c, 0xa4, 0x99, 0x50, 0x42, 0xd7, 0xdf,
+	0x00, 0x17, 0x73, 0xa0, 0x7d, 0x1c, 0x08, 0x39, 0x13, 0x92, 0x96, 0x44, 0xaf, 0x3a, 0x54, 0x78,
+	0xfb, 0x30, 0x12, 0x91, 0xa8, 0xe2, 0xc5, 0xaf, 0x2a, 0x7a, 0xfa, 0xbb, 0x06, 0x3e, 0x22, 0x2c,
+	0x10, 0x49, 0xe8, 0x65, 0xb7, 0x24, 0x25, 0xcc, 0x8b, 0x47, 0xfe, 0x37, 0x2c, 0x50, 0x84, 0x47,
+	0x49, 0x5f, 0x04, 0xfa, 0x31, 0x78, 0x18, 0x5c, 0x7b, 0x3c, 0xa1, 0x3c, 0x3c, 0xd2, 0x4e, 0xb4,
+	0xb3, 0x5d, 0xfc, 0x6e, 0x79, 0x46, 0xa1, 0xfe, 0x19, 0x68, 0x45, 0xb1, 0xf0, 0xbd, 0x98, 0x7e,
+	0xcb, 0x33, 0x95, 0x7b, 0x31, 0x8d, 0x32, 0x91, 0xa7, 0x05, 0x59, 0x3f, 0xd1, 0xce, 0xde, 0xc3,
+	0x87, 0x95, 0x3c, 0xad, 0xd4, 0x41, 0x21, 0xa2, 0x50, 0x7f, 0x02, 0x76, 0x45, 0x59, 0xa2, 0x00,
+	0x77, 0x8a, 0x94, 0x56, 0xe7, 0xe5, 0xeb, 0x6e, 0xed, 0xaf, 0xd7, 0xdd, 0xc6, 0x84, 0x27, 0xea,
+	0x8f, 0x5f, 0x1f, 0x3f, 0x9a, 0x3b, 0x2f, 0x8e, 0xf8, 0x61, 0x45, 0xa3, 0x50, 0x6f, 0x17, 0x5e,
+	0x58, 0x70, 0x23, 0xf3, 0xd9, 0x51, 0xe3, 0x44, 0x3b, 0xdb, 0xc3, 0xcb, 0xf3, 0xe9, 0x6f, 0x1a,
+	0x00, 0x83, 0xe9, 0xe0, 0xd2, 0x4b, 0x53, 0x9e, 0x44, 0xfa, 0x53, 0xd0, 0x91, 0x59, 0x40, 0xff,
+	0xcb, 0x9f, 0x56, 0xfa, 0x6b, 0xc9, 0x2c, 0x18, 0x6c, 0xb2, 0xf8, 0x14, 0x74, 0x42, 0xa9, 0xe8,
+	0xf6, 0xee, 0x5a, 0xa1, 0x54, 0x1b, 0xff, 0xfd, 0x39, 0x68, 0xcb, 0xc5, 0x48, 0xa9, 0x4c, 0xa9,
+	0x1f, 0x4b, 0x2a, 0x79, 0x94, 0x78, 0x2a, 0xcf, 0x58, 0xd9, 0xf1, 0x1e, 0x6e, 0xc9, 0x37, 0x43,
+	0xb7, 0x62, 0x49, 0x16, 0xf2, 0xe9, 0x4f, 0x75, 0xf0, 0xf1, 0xca, 0x85, 0x5c, 0xf2, 0x28, 0xf3,
+	0x14, 0x17, 0x89, 0x95, 0x07, 0x37, 0xec, 0x6d, 0x6e, 0xe5, 0x13, 0x70, 0x50, 0x78, 0x4f, 0x33,
+	0x3e, 0x9b, 0xd7, 0x5f, 0x3a, 0xde, 0x0f, 0xa5, 0x1a, 0x57, 0x71, 0x32, 0x6f, 0x73, 0xdb, 0x90,
+	0x76, 0xfe, 0xd7, 0x90, 0x1a, 0xdb, 0x87, 0xf4, 0x04, 0xec, 0xfa, 0x65, 0x4b, 0x05, 0xfb, 0xe0,
+	0x2d, 0xb6, 0xa0, 0xa2, 0x51, 0x78, 0xfa, 0x8b, 0x06, 0x0e, 0x86, 0x22, 0x58, 0xcf, 0xa8, 0xef,
+	0x83, 0xfa, 0xf2, 0x5e, 0xeb, 0x7c, 0xeb, 0x72, 0xee, 0x6c, 0x59, 0xce, 0x2e, 0x78, 0x54, 0x3c,
+	0x4b, 0x2c, 0xa4, 0x92, 0xbf, 0x60, 0x65, 0x13, 0x0d, 0x0c, 0xaa, 0x10, 0xe1, 0x2f, 0x98, 0x7e,
+	0x0e, 0x0e, 0x94, 0x50, 0x5e, 0x4c, 0x83, 0x6b, 0x2f, 0x8b, 0x58, 0x85, 0x3d, 0x28, 0xb1, 0xf7,
+	0x4b, 0xc1, 0x2e, 0xe3, 0x05, 0x7b, 0x7e, 0x03, 0x00, 0x11, 0x79, 0x16, 0x30, 0xf7, 0x36, 0x65,
+	0xfa, 0x87, 0x40, 0x27, 0xa3, 0x09, 0xb6, 0x21, 0x75, 0xaf, 0xc6, 0x90, 0x8e, 0x30, 0x1a, 0x20,
+	0xa7, 0x59, 0xd3, 0xbb, 0xa0, 0xb3, 0x1a, 0xb7, 0x88, 0x4d, 0x6d, 0x3c, 0x22, 0x84, 0xda, 0xcf,
+	0x4c, 0xe4, 0x34, 0x35, 0xdd, 0x00, 0xed, 0x55, 0xe0, 0x12, 0x61, 0x3c, 0xc2, 0x74, 0x0c, 0x9d,
+	0x3e, 0x72, 0x06, 0xcd, 0x7a, 0xbb, 0xf1, 0xfd, 0xcf, 0x46, 0xed, 0x3c, 0x06, 0x7b, 0xf3, 0x1d,
+	0x51, 0x9e, 0xca, 0xa5, 0x7e, 0x0c, 0x3e, 0xb0, 0x26, 0xf6, 0x73, 0xe8, 0x52, 0xe2, 0x9a, 0xee,
+	0x84, 0x50, 0x1b, 0x43, 0xd3, 0x85, 0xfd, 0x66, 0xad, 0x48, 0xb8, 0x2e, 0xf5, 0x11, 0xb1, 0x47,
+	0x8e, 0x8b, 0x9c, 0x09, 0xec, 0x37, 0x35, 0xbd, 0x03, 0x5a, 0xeb, 0xfa, 0x25, 0x1a, 0x60, 0xd3,
+	0x5d, 0xad, 0xf6, 0x1c, 0xec, 0x63, 0x16, 0xe6, 0x49, 0xe8, 0x25, 0xc1, 0xed, 0xa2, 0x3d, 0x0c,
+	0xfb, 0x13, 0xa7, 0x6f, 0x3a, 0xf6, 0x15, 0x85, 0x76, 0x69, 0xb6, 0x59, 0x2b, 0x92, 0xad, 0xc4,
+	0x31, 0x1c, 0x0f, 0x91, 0x6d, 0x56, 0xa2, 0x36, 0x4f, 0xc6, 0xc1, 0xde, 0xfc, 0xa5, 0xb3, 0xb4,
+	0x3e, 0xb2, 0xbe, 0x84, 0xf6, 0x06, 0xeb, 0x47, 0xe0, 0x70, 0x5d, 0x22, 0xd0, 0x1c, 0x96, 0xa6,
+	0x0d, 0xd0, 0x5e, 0x57, 0xd6, 0x9a, 0x5a, 0xf8, 0xfe, 0x51, 0x03, 0xfb, 0x53, 0x2e, 0xb9, 0xcf,
+	0x63, 0xae, 0x2a, 0xe3, 0x5d, 0xd0, 0x99, 0x22, 0x82, 0x2c, 0x34, 0x44, 0xee, 0x55, 0x35, 0xe2,
+	0x89, 0x43, 0xc6, 0xd0, 0x46, 0x5f, 0xa0, 0xb2, 0xe6, 0x06, 0x60, 0x3c, 0xb1, 0x86, 0xc8, 0xa6,
+	0x18, 0x9a, 0xf3, 0x79, 0xfd, 0x0b, 0xc0, 0x68, 0x6a, 0xba, 0xb0, 0x59, 0xdf, 0x24, 0x22, 0xe7,
+	0x19, 0xc4, 0xc8, 0x6d, 0xee, 0x54, 0xa6, 0x2c, 0xf4, 0xf2, 0xce, 0xd0, 0x5e, 0xdd, 0x19, 0xda,
+	0xdf, 0x77, 0x86, 0xf6, 0xc3, 0xbd, 0x51, 0x7b, 0x75, 0x6f, 0xd4, 0xfe, 0xbc, 0x37, 0x6a, 0x5f,
+	0xf5, 0x22, 0xae, 0xae, 0x73, 0xff, 0x22, 0x10, 0xb3, 0x9e, 0x9f, 0xf8, 0x8f, 0xcb, 0x87, 0xbc,
+	0xb7, 0xf2, 0x65, 0xf8, 0x6e, 0xf9, 0x6d, 0x50, 0xb7, 0x29, 0x93, 0xfe, 0x3b, 0xe5, 0x6b, 0xfd,
+	0xd3, 0x7f, 0x02, 0x00, 0x00, 0xff, 0xff, 0x82, 0x66, 0xda, 0x32, 0x3e, 0x06, 0x00, 0x00,
+}
+
+func (m *SecondarySpSealObjectSignDoc) Marshal() (dAtA []byte, err error) {
 	size := m.Size()
 	dAtA = make([]byte, size)
 	n, err := m.MarshalToSizedBuffer(dAtA[:size])
@@ -355,32 +530,49 @@ func (m *Approval) Marshal() (dAtA []byte, err error) {
 	return dAtA[:n], nil
 }
 
-func (m *Approval) MarshalTo(dAtA []byte) (int, error) {
+func (m *SecondarySpSealObjectSignDoc) MarshalTo(dAtA []byte) (int, error) {
 	size := m.Size()
 	return m.MarshalToSizedBuffer(dAtA[:size])
 }
 
-func (m *Approval) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+func (m *SecondarySpSealObjectSignDoc) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	i := len(dAtA)
 	_ = i
 	var l int
 	_ = l
-	if len(m.Sig) > 0 {
-		i -= len(m.Sig)
-		copy(dAtA[i:], m.Sig)
-		i = encodeVarintCommon(dAtA, i, uint64(len(m.Sig)))
+	if len(m.Checksum) > 0 {
+		i -= len(m.Checksum)
+		copy(dAtA[i:], m.Checksum)
+		i = encodeVarintCommon(dAtA, i, uint64(len(m.Checksum)))
 		i--
-		dAtA[i] = 0x12
+		dAtA[i] = 0x22
 	}
-	if m.ExpiredHeight != 0 {
-		i = encodeVarintCommon(dAtA, i, uint64(m.ExpiredHeight))
+	{
+		size := m.ObjectId.Size()
+		i -= size
+		if _, err := m.ObjectId.MarshalTo(dAtA[i:]); err != nil {
+			return 0, err
+		}
+		i = encodeVarintCommon(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0x1a
+	if m.GlobalVirtualGroupId != 0 {
+		i = encodeVarintCommon(dAtA, i, uint64(m.GlobalVirtualGroupId))
 		i--
-		dAtA[i] = 0x8
+		dAtA[i] = 0x10
+	}
+	if len(m.ChainId) > 0 {
+		i -= len(m.ChainId)
+		copy(dAtA[i:], m.ChainId)
+		i = encodeVarintCommon(dAtA, i, uint64(len(m.ChainId)))
+		i--
+		dAtA[i] = 0xa
 	}
 	return len(dAtA) - i, nil
 }
 
-func (m *SecondarySpSignDoc) Marshal() (dAtA []byte, err error) {
+func (m *GVGMapping) Marshal() (dAtA []byte, err error) {
 	size := m.Size()
 	dAtA = make([]byte, size)
 	n, err := m.MarshalToSizedBuffer(dAtA[:size])
@@ -390,43 +582,134 @@ func (m *SecondarySpSignDoc) Marshal() (dAtA []byte, err error) {
 	return dAtA[:n], nil
 }
 
-func (m *SecondarySpSignDoc) MarshalTo(dAtA []byte) (int, error) {
+func (m *GVGMapping) MarshalTo(dAtA []byte) (int, error) {
 	size := m.Size()
 	return m.MarshalToSizedBuffer(dAtA[:size])
 }
 
-func (m *SecondarySpSignDoc) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+func (m *GVGMapping) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	i := len(dAtA)
 	_ = i
 	var l int
 	_ = l
-	if len(m.Checksum) > 0 {
-		i -= len(m.Checksum)
-		copy(dAtA[i:], m.Checksum)
-		i = encodeVarintCommon(dAtA, i, uint64(len(m.Checksum)))
+	if len(m.SecondarySpBlsSignature) > 0 {
+		i -= len(m.SecondarySpBlsSignature)
+		copy(dAtA[i:], m.SecondarySpBlsSignature)
+		i = encodeVarintCommon(dAtA, i, uint64(len(m.SecondarySpBlsSignature)))
 		i--
 		dAtA[i] = 0x1a
 	}
+	if m.DstGlobalVirtualGroupId != 0 {
+		i = encodeVarintCommon(dAtA, i, uint64(m.DstGlobalVirtualGroupId))
+		i--
+		dAtA[i] = 0x10
+	}
+	if m.SrcGlobalVirtualGroupId != 0 {
+		i = encodeVarintCommon(dAtA, i, uint64(m.SrcGlobalVirtualGroupId))
+		i--
+		dAtA[i] = 0x8
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *SecondarySpMigrationBucketSignDoc) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *SecondarySpMigrationBucketSignDoc) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *SecondarySpMigrationBucketSignDoc) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
 	{
-		size := m.ObjectId.Size()
+		size := m.BucketId.Size()
 		i -= size
-		if _, err := m.ObjectId.MarshalTo(dAtA[i:]); err != nil {
+		if _, err := m.BucketId.MarshalTo(dAtA[i:]); err != nil {
 			return 0, err
 		}
 		i = encodeVarintCommon(dAtA, i, uint64(size))
 	}
 	i--
-	dAtA[i] = 0x12
-	if len(m.SpAddress) > 0 {
-		i -= len(m.SpAddress)
-		copy(dAtA[i:], m.SpAddress)
-		i = encodeVarintCommon(dAtA, i, uint64(len(m.SpAddress)))
+	dAtA[i] = 0x2a
+	if m.DstGlobalVirtualGroupId != 0 {
+		i = encodeVarintCommon(dAtA, i, uint64(m.DstGlobalVirtualGroupId))
+		i--
+		dAtA[i] = 0x20
+	}
+	if m.SrcGlobalVirtualGroupId != 0 {
+		i = encodeVarintCommon(dAtA, i, uint64(m.SrcGlobalVirtualGroupId))
+		i--
+		dAtA[i] = 0x18
+	}
+	if m.DstPrimarySpId != 0 {
+		i = encodeVarintCommon(dAtA, i, uint64(m.DstPrimarySpId))
+		i--
+		dAtA[i] = 0x10
+	}
+	if len(m.ChainId) > 0 {
+		i -= len(m.ChainId)
+		copy(dAtA[i:], m.ChainId)
+		i = encodeVarintCommon(dAtA, i, uint64(len(m.ChainId)))
 		i--
 		dAtA[i] = 0xa
 	}
 	return len(dAtA) - i, nil
 }
 
+func (m *LocalVirtualGroup) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *LocalVirtualGroup) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *LocalVirtualGroup) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.TotalChargeSize != 0 {
+		i = encodeVarintCommon(dAtA, i, uint64(m.TotalChargeSize))
+		i--
+		dAtA[i] = 0x28
+	}
+	if m.StoredSize != 0 {
+		i = encodeVarintCommon(dAtA, i, uint64(m.StoredSize))
+		i--
+		dAtA[i] = 0x20
+	}
+	if m.GlobalVirtualGroupId != 0 {
+		i = encodeVarintCommon(dAtA, i, uint64(m.GlobalVirtualGroupId))
+		i--
+		dAtA[i] = 0x18
+	}
+	if m.Id != 0 {
+		i = encodeVarintCommon(dAtA, i, uint64(m.Id))
+		i--
+		dAtA[i] = 0x8
+	}
+	return len(dAtA) - i, nil
+}
+
 func encodeVarintCommon(dAtA []byte, offset int, v uint64) int {
 	offset -= sovCommon(v)
 	base := offset
@@ -438,38 +721,89 @@ func encodeVarintCommon(dAtA []byte, offset int, v uint64) int {
 	dAtA[offset] = uint8(v)
 	return base
 }
-func (m *Approval) Size() (n int) {
+func (m *SecondarySpSealObjectSignDoc) Size() (n int) {
 	if m == nil {
 		return 0
 	}
 	var l int
 	_ = l
-	if m.ExpiredHeight != 0 {
-		n += 1 + sovCommon(uint64(m.ExpiredHeight))
+	l = len(m.ChainId)
+	if l > 0 {
+		n += 1 + l + sovCommon(uint64(l))
+	}
+	if m.GlobalVirtualGroupId != 0 {
+		n += 1 + sovCommon(uint64(m.GlobalVirtualGroupId))
 	}
-	l = len(m.Sig)
+	l = m.ObjectId.Size()
+	n += 1 + l + sovCommon(uint64(l))
+	l = len(m.Checksum)
 	if l > 0 {
 		n += 1 + l + sovCommon(uint64(l))
 	}
 	return n
 }
 
-func (m *SecondarySpSignDoc) Size() (n int) {
+func (m *GVGMapping) Size() (n int) {
 	if m == nil {
 		return 0
 	}
 	var l int
 	_ = l
-	l = len(m.SpAddress)
+	if m.SrcGlobalVirtualGroupId != 0 {
+		n += 1 + sovCommon(uint64(m.SrcGlobalVirtualGroupId))
+	}
+	if m.DstGlobalVirtualGroupId != 0 {
+		n += 1 + sovCommon(uint64(m.DstGlobalVirtualGroupId))
+	}
+	l = len(m.SecondarySpBlsSignature)
 	if l > 0 {
 		n += 1 + l + sovCommon(uint64(l))
 	}
-	l = m.ObjectId.Size()
-	n += 1 + l + sovCommon(uint64(l))
-	l = len(m.Checksum)
+	return n
+}
+
+func (m *SecondarySpMigrationBucketSignDoc) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.ChainId)
 	if l > 0 {
 		n += 1 + l + sovCommon(uint64(l))
 	}
+	if m.DstPrimarySpId != 0 {
+		n += 1 + sovCommon(uint64(m.DstPrimarySpId))
+	}
+	if m.SrcGlobalVirtualGroupId != 0 {
+		n += 1 + sovCommon(uint64(m.SrcGlobalVirtualGroupId))
+	}
+	if m.DstGlobalVirtualGroupId != 0 {
+		n += 1 + sovCommon(uint64(m.DstGlobalVirtualGroupId))
+	}
+	l = m.BucketId.Size()
+	n += 1 + l + sovCommon(uint64(l))
+	return n
+}
+
+func (m *LocalVirtualGroup) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.Id != 0 {
+		n += 1 + sovCommon(uint64(m.Id))
+	}
+	if m.GlobalVirtualGroupId != 0 {
+		n += 1 + sovCommon(uint64(m.GlobalVirtualGroupId))
+	}
+	if m.StoredSize != 0 {
+		n += 1 + sovCommon(uint64(m.StoredSize))
+	}
+	if m.TotalChargeSize != 0 {
+		n += 1 + sovCommon(uint64(m.TotalChargeSize))
+	}
 	return n
 }
 
@@ -479,7 +813,7 @@ func sovCommon(x uint64) (n int) {
 func sozCommon(x uint64) (n int) {
 	return sovCommon(uint64((x << 1) ^ uint64((int64(x) >> 63))))
 }
-func (m *Approval) Unmarshal(dAtA []byte) error {
+func (m *SecondarySpSealObjectSignDoc) Unmarshal(dAtA []byte) error {
 	l := len(dAtA)
 	iNdEx := 0
 	for iNdEx < l {
@@ -502,17 +836,17 @@ func (m *Approval) Unmarshal(dAtA []byte) error {
 		fieldNum := int32(wire >> 3)
 		wireType := int(wire & 0x7)
 		if wireType == 4 {
-			return fmt.Errorf("proto: Approval: wiretype end group for non-group")
+			return fmt.Errorf("proto: SecondarySpSealObjectSignDoc: wiretype end group for non-group")
 		}
 		if fieldNum <= 0 {
-			return fmt.Errorf("proto: Approval: illegal tag %d (wire type %d)", fieldNum, wire)
+			return fmt.Errorf("proto: SecondarySpSealObjectSignDoc: illegal tag %d (wire type %d)", fieldNum, wire)
 		}
 		switch fieldNum {
 		case 1:
-			if wireType != 0 {
-				return fmt.Errorf("proto: wrong wireType = %d for field ExpiredHeight", wireType)
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType)
 			}
-			m.ExpiredHeight = 0
+			var stringLen uint64
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowCommon
@@ -522,16 +856,29 @@ func (m *Approval) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				m.ExpiredHeight |= uint64(b&0x7F) << shift
+				stringLen |= uint64(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthCommon
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthCommon
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.ChainId = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
 		case 2:
-			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field Sig", wireType)
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupId", wireType)
 			}
-			var byteLen int
+			m.GlobalVirtualGroupId = 0
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowCommon
@@ -541,24 +888,199 @@ func (m *Approval) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				byteLen |= int(b&0x7F) << shift
+				m.GlobalVirtualGroupId |= uint32(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
-			if byteLen < 0 {
-				return ErrInvalidLengthCommon
-			}
-			postIndex := iNdEx + byteLen
-			if postIndex < 0 {
-				return ErrInvalidLengthCommon
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ObjectId", wireType)
 			}
-			if postIndex > l {
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowCommon
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthCommon
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthCommon
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.ObjectId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 4:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Checksum", wireType)
+			}
+			var byteLen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowCommon
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				byteLen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if byteLen < 0 {
+				return ErrInvalidLengthCommon
+			}
+			postIndex := iNdEx + byteLen
+			if postIndex < 0 {
+				return ErrInvalidLengthCommon
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Checksum = append(m.Checksum[:0], dAtA[iNdEx:postIndex]...)
+			if m.Checksum == nil {
+				m.Checksum = []byte{}
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipCommon(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthCommon
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *GVGMapping) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowCommon
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: GVGMapping: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: GVGMapping: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field SrcGlobalVirtualGroupId", wireType)
+			}
+			m.SrcGlobalVirtualGroupId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowCommon
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.SrcGlobalVirtualGroupId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field DstGlobalVirtualGroupId", wireType)
+			}
+			m.DstGlobalVirtualGroupId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowCommon
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.DstGlobalVirtualGroupId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field SecondarySpBlsSignature", wireType)
+			}
+			var byteLen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowCommon
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				byteLen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if byteLen < 0 {
+				return ErrInvalidLengthCommon
+			}
+			postIndex := iNdEx + byteLen
+			if postIndex < 0 {
+				return ErrInvalidLengthCommon
+			}
+			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			m.Sig = append(m.Sig[:0], dAtA[iNdEx:postIndex]...)
-			if m.Sig == nil {
-				m.Sig = []byte{}
+			m.SecondarySpBlsSignature = append(m.SecondarySpBlsSignature[:0], dAtA[iNdEx:postIndex]...)
+			if m.SecondarySpBlsSignature == nil {
+				m.SecondarySpBlsSignature = []byte{}
 			}
 			iNdEx = postIndex
 		default:
@@ -582,7 +1104,7 @@ func (m *Approval) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
-func (m *SecondarySpSignDoc) Unmarshal(dAtA []byte) error {
+func (m *SecondarySpMigrationBucketSignDoc) Unmarshal(dAtA []byte) error {
 	l := len(dAtA)
 	iNdEx := 0
 	for iNdEx < l {
@@ -605,15 +1127,15 @@ func (m *SecondarySpSignDoc) Unmarshal(dAtA []byte) error {
 		fieldNum := int32(wire >> 3)
 		wireType := int(wire & 0x7)
 		if wireType == 4 {
-			return fmt.Errorf("proto: SecondarySpSignDoc: wiretype end group for non-group")
+			return fmt.Errorf("proto: SecondarySpMigrationBucketSignDoc: wiretype end group for non-group")
 		}
 		if fieldNum <= 0 {
-			return fmt.Errorf("proto: SecondarySpSignDoc: illegal tag %d (wire type %d)", fieldNum, wire)
+			return fmt.Errorf("proto: SecondarySpMigrationBucketSignDoc: illegal tag %d (wire type %d)", fieldNum, wire)
 		}
 		switch fieldNum {
 		case 1:
 			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field SpAddress", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType)
 			}
 			var stringLen uint64
 			for shift := uint(0); ; shift += 7 {
@@ -641,11 +1163,68 @@ func (m *SecondarySpSignDoc) Unmarshal(dAtA []byte) error {
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			m.SpAddress = string(dAtA[iNdEx:postIndex])
+			m.ChainId = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
 		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field DstPrimarySpId", wireType)
+			}
+			m.DstPrimarySpId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowCommon
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.DstPrimarySpId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 3:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field SrcGlobalVirtualGroupId", wireType)
+			}
+			m.SrcGlobalVirtualGroupId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowCommon
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.SrcGlobalVirtualGroupId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 4:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field DstGlobalVirtualGroupId", wireType)
+			}
+			m.DstGlobalVirtualGroupId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowCommon
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.DstGlobalVirtualGroupId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 5:
 			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field ObjectId", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field BucketId", wireType)
 			}
 			var stringLen uint64
 			for shift := uint(0); ; shift += 7 {
@@ -673,15 +1252,84 @@ func (m *SecondarySpSignDoc) Unmarshal(dAtA []byte) error {
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			if err := m.ObjectId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+			if err := m.BucketId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
 				return err
 			}
 			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipCommon(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthCommon
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *LocalVirtualGroup) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowCommon
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: LocalVirtualGroup: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: LocalVirtualGroup: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType)
+			}
+			m.Id = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowCommon
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.Id |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
 		case 3:
-			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field Checksum", wireType)
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupId", wireType)
 			}
-			var byteLen int
+			m.GlobalVirtualGroupId = 0
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowCommon
@@ -691,26 +1339,49 @@ func (m *SecondarySpSignDoc) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				byteLen |= int(b&0x7F) << shift
+				m.GlobalVirtualGroupId |= uint32(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
-			if byteLen < 0 {
-				return ErrInvalidLengthCommon
+		case 4:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field StoredSize", wireType)
 			}
-			postIndex := iNdEx + byteLen
-			if postIndex < 0 {
-				return ErrInvalidLengthCommon
+			m.StoredSize = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowCommon
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.StoredSize |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
 			}
-			if postIndex > l {
-				return io.ErrUnexpectedEOF
+		case 5:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field TotalChargeSize", wireType)
 			}
-			m.Checksum = append(m.Checksum[:0], dAtA[iNdEx:postIndex]...)
-			if m.Checksum == nil {
-				m.Checksum = []byte{}
+			m.TotalChargeSize = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowCommon
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.TotalChargeSize |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
 			}
-			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipCommon(dAtA[iNdEx:])
diff --git a/x/storage/types/crosschain.go b/x/storage/types/crosschain.go
index acdc274e1..a09c1ae2c 100644
--- a/x/storage/types/crosschain.go
+++ b/x/storage/types/crosschain.go
@@ -4,9 +4,12 @@ import (
 	"math/big"
 
 	"cosmossdk.io/errors"
-	"github.com/cosmos/cosmos-sdk/bsc/rlp"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	"github.com/ethereum/go-ethereum/accounts/abi"
+	"github.com/ethereum/go-ethereum/common"
+
+	gnfdcommon "github.com/bnb-chain/greenfield/types/common"
 )
 
 const (
@@ -38,24 +41,26 @@ const (
 	OperationUpdateGroupMember uint8 = 4
 )
 
+func SafeBigInt(input *big.Int) *big.Int {
+	if input == nil {
+		return big.NewInt(0)
+	}
+	return input
+}
+
 type CrossChainPackage struct {
 	OperationType uint8
 	Package       []byte
 }
 
 func (p CrossChainPackage) MustSerialize() []byte {
-	encodedBytes, err := rlp.EncodeToBytes(p)
-	if err != nil {
-		panic("encode delete cross chain package error")
-	}
-	return encodedBytes
+	return append([]byte{p.OperationType}, p.Package...)
 }
 
 func DeserializeRawCrossChainPackage(serializedPackage []byte) (*CrossChainPackage, error) {
-	var tp CrossChainPackage
-	err := rlp.DecodeBytes(serializedPackage, &tp)
-	if err != nil {
-		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "deserialize raw cross chain package failed")
+	tp := CrossChainPackage{
+		OperationType: serializedPackage[0],
+		Package:       serializedPackage[1:],
 	}
 	return &tp, nil
 }
@@ -144,26 +149,89 @@ type MirrorBucketSynPackage struct {
 	Owner sdk.AccAddress
 }
 
+type GeneralMirrorSynPackageStruct struct {
+	Id    *big.Int
+	Owner common.Address
+}
+
 type MirrorBucketAckPackage struct {
 	Status uint8
 	Id     *big.Int
 }
 
+var (
+	generalMirrorSynPackageStructType, _ = abi.NewType("tuple", "", []abi.ArgumentMarshaling{
+		{Name: "Id", Type: "uint256"},
+		{Name: "Owner", Type: "address"},
+	})
+
+	generalMirrorSynPackageArgs = abi.Arguments{
+		{Type: generalMirrorSynPackageStructType},
+	}
+
+	generalMirrorAckPackageType, _ = abi.NewType("tuple", "", []abi.ArgumentMarshaling{
+		{Name: "Status", Type: "uint8"},
+		{Name: "Id", Type: "uint256"},
+	})
+
+	generalMirrorAckPackageArgs = abi.Arguments{
+		{Type: generalMirrorAckPackageType},
+	}
+)
+
+func (pkg *MirrorBucketSynPackage) Serialize() ([]byte, error) {
+	return generalMirrorSynPackageArgs.Pack(&GeneralMirrorSynPackageStruct{
+		SafeBigInt(pkg.Id),
+		common.BytesToAddress(pkg.Owner),
+	})
+}
+
+func deserializeMirrorSynPackage(serializedPackage []byte) (*GeneralMirrorSynPackageStruct, error) {
+	unpacked, err := generalMirrorSynPackageArgs.Unpack(serializedPackage)
+	if err != nil {
+		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "deserialize mirror syn package failed")
+	}
+
+	unpackedStruct := abi.ConvertType(unpacked[0], GeneralMirrorSynPackageStruct{})
+	pkgStruct, ok := unpackedStruct.(GeneralMirrorSynPackageStruct)
+	if !ok {
+		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "reflect mirror syn package failed")
+	}
+	return &pkgStruct, nil
+}
+
 func DeserializeMirrorBucketSynPackage(serializedPackage []byte) (interface{}, error) {
-	var tp MirrorBucketSynPackage
-	err := rlp.DecodeBytes(serializedPackage, &tp)
+	pkgStruct, err := deserializeMirrorSynPackage(serializedPackage)
 	if err != nil {
-		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "deserialize mirror bucket syn package failed")
+		return nil, err
+	}
+
+	tp := MirrorBucketSynPackage{
+		pkgStruct.Id,
+		pkgStruct.Owner.Bytes(),
 	}
 	return &tp, nil
 }
 
+func (pkg *MirrorBucketAckPackage) Serialize() ([]byte, error) {
+	return generalMirrorAckPackageArgs.Pack(&MirrorBucketAckPackage{
+		pkg.Status,
+		SafeBigInt(pkg.Id),
+	})
+}
+
 func DeserializeMirrorBucketAckPackage(serializedPackage []byte) (interface{}, error) {
-	var tp MirrorBucketAckPackage
-	err := rlp.DecodeBytes(serializedPackage, &tp)
+	unpacked, err := generalMirrorAckPackageArgs.Unpack(serializedPackage)
 	if err != nil {
 		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "deserialize mirror bucket ack package failed")
 	}
+
+	unpackedStruct := abi.ConvertType(unpacked[0], MirrorBucketAckPackage{})
+	tp, ok := unpackedStruct.(MirrorBucketAckPackage)
+	if !ok {
+		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "reflect mirror bucket ack package failed")
+	}
+
 	return &tp, nil
 }
 
@@ -177,21 +245,45 @@ type MirrorObjectAckPackage struct {
 	Id     *big.Int
 }
 
+func (pkg *MirrorObjectSynPackage) Serialize() ([]byte, error) {
+	return generalMirrorSynPackageArgs.Pack(&GeneralMirrorSynPackageStruct{
+		SafeBigInt(pkg.Id),
+		common.BytesToAddress(pkg.Owner),
+	})
+}
+
 func DeserializeMirrorObjectSynPackage(serializedPackage []byte) (interface{}, error) {
-	var tp MirrorObjectSynPackage
-	err := rlp.DecodeBytes(serializedPackage, &tp)
+	pkgStruct, err := deserializeMirrorSynPackage(serializedPackage)
 	if err != nil {
-		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "deserialize mirror object syn package failed")
+		return nil, err
+	}
+
+	tp := MirrorObjectSynPackage{
+		pkgStruct.Id,
+		pkgStruct.Owner.Bytes(),
 	}
 	return &tp, nil
 }
 
+func (pkg *MirrorObjectAckPackage) Serialize() ([]byte, error) {
+	return generalMirrorAckPackageArgs.Pack(&MirrorObjectAckPackage{
+		pkg.Status,
+		SafeBigInt(pkg.Id),
+	})
+}
+
 func DeserializeMirrorObjectAckPackage(serializedPackage []byte) (interface{}, error) {
-	var tp MirrorObjectAckPackage
-	err := rlp.DecodeBytes(serializedPackage, &tp)
+	unpacked, err := generalMirrorAckPackageArgs.Unpack(serializedPackage)
 	if err != nil {
 		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "deserialize mirror object ack package failed")
 	}
+
+	unpackedStruct := abi.ConvertType(unpacked[0], MirrorObjectAckPackage{})
+	tp, ok := unpackedStruct.(MirrorObjectAckPackage)
+	if !ok {
+		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "reflect mirror object ack package failed")
+	}
+
 	return &tp, nil
 }
 
@@ -205,21 +297,45 @@ type MirrorGroupAckPackage struct {
 	Id     *big.Int
 }
 
+func (pkg *MirrorGroupSynPackage) Serialize() ([]byte, error) {
+	return generalMirrorSynPackageArgs.Pack(&GeneralMirrorSynPackageStruct{
+		SafeBigInt(pkg.Id),
+		common.BytesToAddress(pkg.Owner),
+	})
+}
+
 func DeserializeMirrorGroupSynPackage(serializedPackage []byte) (interface{}, error) {
-	var tp MirrorGroupSynPackage
-	err := rlp.DecodeBytes(serializedPackage, &tp)
+	pkgStruct, err := deserializeMirrorSynPackage(serializedPackage)
 	if err != nil {
-		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "deserialize mirror group syn package failed")
+		return nil, err
+	}
+
+	tp := MirrorGroupSynPackage{
+		pkgStruct.Id,
+		pkgStruct.Owner.Bytes(),
 	}
 	return &tp, nil
 }
 
+func (pkg *MirrorGroupAckPackage) Serialize() ([]byte, error) {
+	return generalMirrorAckPackageArgs.Pack(&MirrorGroupAckPackage{
+		pkg.Status,
+		SafeBigInt(pkg.Id),
+	})
+}
+
 func DeserializeMirrorGroupAckPackage(serializedPackage []byte) (interface{}, error) {
-	var tp MirrorGroupAckPackage
-	err := rlp.DecodeBytes(serializedPackage, &tp)
+	unpacked, err := generalMirrorAckPackageArgs.Unpack(serializedPackage)
 	if err != nil {
 		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "deserialize mirror group ack package failed")
 	}
+
+	unpackedStruct := abi.ConvertType(unpacked[0], MirrorGroupAckPackage{})
+	tp, ok := unpackedStruct.(MirrorGroupAckPackage)
+	if !ok {
+		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "reflect mirror group ack package failed")
+	}
+
 	return &tp, nil
 }
 
@@ -235,6 +351,36 @@ type CreateBucketSynPackage struct {
 	ExtraData                      []byte
 }
 
+type CreateBucketSynPackageStruct struct {
+	Creator                        common.Address
+	BucketName                     string
+	Visibility                     uint32
+	PaymentAddress                 common.Address
+	PrimarySpAddress               common.Address
+	PrimarySpApprovalExpiredHeight uint64
+	PrimarySpApprovalSignature     []byte
+	ChargedReadQuota               uint64
+	ExtraData                      []byte
+}
+
+var (
+	createBucketSynPackageStructType, _ = abi.NewType("tuple", "", []abi.ArgumentMarshaling{
+		{Name: "Creator", Type: "address"},
+		{Name: "BucketName", Type: "string"},
+		{Name: "Visibility", Type: "uint32"},
+		{Name: "PaymentAddress", Type: "address"},
+		{Name: "PrimarySpAddress", Type: "address"},
+		{Name: "PrimarySpApprovalExpiredHeight", Type: "uint64"},
+		{Name: "PrimarySpApprovalSignature", Type: "bytes"},
+		{Name: "ChargedReadQuota", Type: "uint64"},
+		{Name: "ExtraData", Type: "bytes"},
+	})
+
+	createBucketSynPackageStructArgs = abi.Arguments{
+		{Type: createBucketSynPackageStructType},
+	}
+)
+
 func (p CreateBucketSynPackage) ValidateBasic() error {
 	msg := MsgCreateBucket{
 		Creator:          p.Creator.String(),
@@ -242,7 +388,7 @@ func (p CreateBucketSynPackage) ValidateBasic() error {
 		Visibility:       VisibilityType(p.Visibility),
 		PaymentAddress:   p.PaymentAddress.String(),
 		PrimarySpAddress: p.PrimarySpAddress.String(),
-		PrimarySpApproval: &Approval{
+		PrimarySpApproval: &gnfdcommon.Approval{
 			ExpiredHeight: p.PrimarySpApprovalExpiredHeight,
 			Sig:           p.PrimarySpApprovalSignature,
 		},
@@ -259,7 +405,7 @@ func (p CreateBucketSynPackage) GetApprovalBytes() []byte {
 		Visibility:       VisibilityType(p.Visibility),
 		PaymentAddress:   p.PaymentAddress.String(),
 		PrimarySpAddress: p.PrimarySpAddress.String(),
-		PrimarySpApproval: &Approval{
+		PrimarySpApproval: &gnfdcommon.Approval{
 			ExpiredHeight: p.PrimarySpApprovalExpiredHeight,
 			Sig:           p.PrimarySpApprovalSignature,
 		},
@@ -269,11 +415,28 @@ func (p CreateBucketSynPackage) GetApprovalBytes() []byte {
 }
 
 func DeserializeCreateBucketSynPackage(serializedPackage []byte) (interface{}, error) {
-	var tp CreateBucketSynPackage
-	err := rlp.DecodeBytes(serializedPackage, &tp)
+	unpacked, err := createBucketSynPackageStructArgs.Unpack(serializedPackage)
 	if err != nil {
 		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "deserialize create bucket syn package failed")
 	}
+
+	unpackedStruct := abi.ConvertType(unpacked[0], CreateBucketSynPackageStruct{})
+	pkgStruct, ok := unpackedStruct.(CreateBucketSynPackageStruct)
+	if !ok {
+		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "reflect create bucket syn package failed")
+	}
+
+	tp := CreateBucketSynPackage{
+		pkgStruct.Creator.Bytes(),
+		pkgStruct.BucketName,
+		pkgStruct.Visibility,
+		pkgStruct.PaymentAddress.Bytes(),
+		pkgStruct.PrimarySpAddress.Bytes(),
+		pkgStruct.PrimarySpApprovalExpiredHeight,
+		pkgStruct.PrimarySpApprovalSignature,
+		pkgStruct.ChargedReadQuota,
+		pkgStruct.ExtraData,
+	}
 	return &tp, nil
 }
 
@@ -284,8 +447,33 @@ type CreateBucketAckPackage struct {
 	ExtraData []byte
 }
 
+type GeneralCreateAckPackageStruct struct {
+	Status    uint8
+	Id        *big.Int
+	Creator   common.Address
+	ExtraData []byte
+}
+
+var (
+	generalCreateAckPackageType, _ = abi.NewType("tuple", "", []abi.ArgumentMarshaling{
+		{Name: "Status", Type: "uint8"},
+		{Name: "Id", Type: "uint256"},
+		{Name: "Creator", Type: "address"},
+		{Name: "ExtraData", Type: "bytes"},
+	})
+
+	generalCreateAckPackageArgs = abi.Arguments{
+		{Type: generalCreateAckPackageType},
+	}
+)
+
 func (p CreateBucketAckPackage) MustSerialize() []byte {
-	encodedBytes, err := rlp.EncodeToBytes(p)
+	encodedBytes, err := generalCreateAckPackageArgs.Pack(&GeneralCreateAckPackageStruct{
+		Status:    p.Status,
+		Id:        SafeBigInt(p.Id),
+		Creator:   common.BytesToAddress(p.Creator),
+		ExtraData: p.ExtraData,
+	})
 	if err != nil {
 		panic("encode create bucket ack package error")
 	}
@@ -293,11 +481,23 @@ func (p CreateBucketAckPackage) MustSerialize() []byte {
 }
 
 func DeserializeCreateBucketAckPackage(serializedPackage []byte) (interface{}, error) {
-	var tp CreateBucketAckPackage
-	err := rlp.DecodeBytes(serializedPackage, &tp)
+	unpacked, err := generalCreateAckPackageArgs.Unpack(serializedPackage)
 	if err != nil {
 		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "deserialize create bucket ack package failed")
 	}
+
+	unpackedStruct := abi.ConvertType(unpacked[0], GeneralCreateAckPackageStruct{})
+	pkgStruct, ok := unpackedStruct.(GeneralCreateAckPackageStruct)
+	if !ok {
+		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "reflect create bucket ack package failed")
+	}
+
+	tp := CreateBucketAckPackage{
+		Status:    pkgStruct.Status,
+		Id:        pkgStruct.Id,
+		Creator:   pkgStruct.Creator.Bytes(),
+		ExtraData: pkgStruct.ExtraData,
+	}
 	return &tp, nil
 }
 
@@ -307,6 +507,24 @@ type DeleteBucketSynPackage struct {
 	ExtraData []byte
 }
 
+type GeneralDeleteSynPackageStruct struct {
+	Operator  common.Address
+	Id        *big.Int
+	ExtraData []byte
+}
+
+var (
+	generalDeleteSynPackageType, _ = abi.NewType("tuple", "", []abi.ArgumentMarshaling{
+		{Name: "Operator", Type: "address"},
+		{Name: "Id", Type: "uint256"},
+		{Name: "ExtraData", Type: "bytes"},
+	})
+
+	generalDeleteSynPackageArgs = abi.Arguments{
+		{Type: generalDeleteSynPackageType},
+	}
+)
+
 func (p DeleteBucketSynPackage) ValidateBasic() error {
 	if p.Operator.Empty() {
 		return sdkerrors.ErrInvalidAddress
@@ -318,11 +536,22 @@ func (p DeleteBucketSynPackage) ValidateBasic() error {
 }
 
 func DeserializeDeleteBucketSynPackage(serializedPackage []byte) (interface{}, error) {
-	var tp DeleteBucketSynPackage
-	err := rlp.DecodeBytes(serializedPackage, &tp)
+	unpacked, err := generalDeleteSynPackageArgs.Unpack(serializedPackage)
 	if err != nil {
 		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "deserialize delete bucket syn package failed")
 	}
+
+	unpackedStruct := abi.ConvertType(unpacked[0], GeneralDeleteSynPackageStruct{})
+	pkgStruct, ok := unpackedStruct.(GeneralDeleteSynPackageStruct)
+	if !ok {
+		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "reflect delete bucket syn package failed")
+	}
+
+	tp := DeleteBucketSynPackage{
+		Operator:  pkgStruct.Operator.Bytes(),
+		Id:        pkgStruct.Id,
+		ExtraData: pkgStruct.ExtraData,
+	}
 	return &tp, nil
 }
 
@@ -332,8 +561,24 @@ type DeleteBucketAckPackage struct {
 	ExtraData []byte
 }
 
+var (
+	generalDeleteAckPackageType, _ = abi.NewType("tuple", "", []abi.ArgumentMarshaling{
+		{Name: "Status", Type: "uint8"},
+		{Name: "Id", Type: "uint256"},
+		{Name: "ExtraData", Type: "bytes"},
+	})
+
+	generalDeleteAckPackageArgs = abi.Arguments{
+		{Type: generalDeleteAckPackageType},
+	}
+)
+
 func (p DeleteBucketAckPackage) MustSerialize() []byte {
-	encodedBytes, err := rlp.EncodeToBytes(p)
+	encodedBytes, err := generalCreateAckPackageArgs.Pack(&DeleteBucketAckPackage{
+		p.Status,
+		SafeBigInt(p.Id),
+		p.ExtraData,
+	})
 	if err != nil {
 		panic("encode delete bucket ack package error")
 	}
@@ -341,11 +586,16 @@ func (p DeleteBucketAckPackage) MustSerialize() []byte {
 }
 
 func DeserializeDeleteBucketAckPackage(serializedPackage []byte) (interface{}, error) {
-	var tp DeleteBucketAckPackage
-	err := rlp.DecodeBytes(serializedPackage, &tp)
+	unpacked, err := generalDeleteAckPackageArgs.Unpack(serializedPackage)
 	if err != nil {
 		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "deserialize delete bucket ack package failed")
 	}
+
+	unpackedStruct := abi.ConvertType(unpacked[0], DeleteBucketAckPackage{})
+	tp, ok := unpackedStruct.(DeleteBucketAckPackage)
+	if !ok {
+		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "reflect delete bucket ack package failed")
+	}
 	return &tp, nil
 }
 
@@ -355,6 +605,24 @@ type CreateGroupSynPackage struct {
 	ExtraData []byte
 }
 
+type CreateGroupSynPackageStruct struct {
+	Creator   common.Address
+	GroupName string
+	ExtraData []byte
+}
+
+var (
+	createGroupSynPackageType, _ = abi.NewType("tuple", "", []abi.ArgumentMarshaling{
+		{Name: "Creator", Type: "address"},
+		{Name: "GroupName", Type: "string"},
+		{Name: "ExtraData", Type: "bytes"},
+	})
+
+	createGroupSynPackageArgs = abi.Arguments{
+		{Type: createGroupSynPackageType},
+	}
+)
+
 func (p CreateGroupSynPackage) ValidateBasic() error {
 	msg := MsgCreateGroup{
 		Creator:   p.Creator.String(),
@@ -364,11 +632,22 @@ func (p CreateGroupSynPackage) ValidateBasic() error {
 }
 
 func DeserializeCreateGroupSynPackage(serializedPackage []byte) (interface{}, error) {
-	var tp CreateGroupSynPackage
-	err := rlp.DecodeBytes(serializedPackage, &tp)
+	unpacked, err := createGroupSynPackageArgs.Unpack(serializedPackage)
 	if err != nil {
 		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "deserialize create group syn package failed")
 	}
+
+	unpackedStruct := abi.ConvertType(unpacked[0], CreateGroupSynPackageStruct{})
+	pkgStruct, ok := unpackedStruct.(CreateGroupSynPackageStruct)
+	if !ok {
+		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "reflect create group syn package failed")
+	}
+
+	tp := CreateGroupSynPackage{
+		pkgStruct.Creator.Bytes(),
+		pkgStruct.GroupName,
+		pkgStruct.ExtraData,
+	}
 	return &tp, nil
 }
 
@@ -380,7 +659,12 @@ type CreateGroupAckPackage struct {
 }
 
 func (p CreateGroupAckPackage) MustSerialize() []byte {
-	encodedBytes, err := rlp.EncodeToBytes(p)
+	encodedBytes, err := generalCreateAckPackageArgs.Pack(&GeneralCreateAckPackageStruct{
+		Status:    p.Status,
+		Id:        SafeBigInt(p.Id),
+		Creator:   common.BytesToAddress(p.Creator),
+		ExtraData: p.ExtraData,
+	})
 	if err != nil {
 		panic("encode create group ack package error")
 	}
@@ -388,11 +672,23 @@ func (p CreateGroupAckPackage) MustSerialize() []byte {
 }
 
 func DeserializeCreateGroupAckPackage(serializedPackage []byte) (interface{}, error) {
-	var tp CreateGroupAckPackage
-	err := rlp.DecodeBytes(serializedPackage, &tp)
+	unpacked, err := generalCreateAckPackageArgs.Unpack(serializedPackage)
 	if err != nil {
 		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "deserialize create group ack package failed")
 	}
+
+	unpackedStruct := abi.ConvertType(unpacked[0], GeneralCreateAckPackageStruct{})
+	pkgStruct, ok := unpackedStruct.(GeneralCreateAckPackageStruct)
+	if !ok {
+		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "reflect create group ack package failed")
+	}
+
+	tp := CreateGroupAckPackage{
+		Status:    pkgStruct.Status,
+		Id:        pkgStruct.Id,
+		Creator:   pkgStruct.Creator.Bytes(),
+		ExtraData: pkgStruct.ExtraData,
+	}
 	return &tp, nil
 }
 
@@ -413,11 +709,22 @@ func (p DeleteObjectSynPackage) ValidateBasic() error {
 }
 
 func DeserializeDeleteObjectSynPackage(serializedPackage []byte) (interface{}, error) {
-	var tp DeleteObjectSynPackage
-	err := rlp.DecodeBytes(serializedPackage, &tp)
+	unpacked, err := generalDeleteSynPackageArgs.Unpack(serializedPackage)
 	if err != nil {
 		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "deserialize delete object syn package failed")
 	}
+
+	unpackedStruct := abi.ConvertType(unpacked[0], GeneralDeleteSynPackageStruct{})
+	pkgStruct, ok := unpackedStruct.(GeneralDeleteSynPackageStruct)
+	if !ok {
+		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "reflect delete object syn package failed")
+	}
+
+	tp := DeleteObjectSynPackage{
+		Operator:  pkgStruct.Operator.Bytes(),
+		Id:        pkgStruct.Id,
+		ExtraData: pkgStruct.ExtraData,
+	}
 	return &tp, nil
 }
 
@@ -428,7 +735,11 @@ type DeleteObjectAckPackage struct {
 }
 
 func (p DeleteObjectAckPackage) MustSerialize() []byte {
-	encodedBytes, err := rlp.EncodeToBytes(p)
+	encodedBytes, err := generalDeleteAckPackageArgs.Pack(&DeleteObjectAckPackage{
+		p.Status,
+		SafeBigInt(p.Id),
+		p.ExtraData,
+	})
 	if err != nil {
 		panic("encode delete object ack package error")
 	}
@@ -436,10 +747,15 @@ func (p DeleteObjectAckPackage) MustSerialize() []byte {
 }
 
 func DeserializeDeleteObjectAckPackage(serializedPackage []byte) (interface{}, error) {
-	var tp DeleteObjectAckPackage
-	err := rlp.DecodeBytes(serializedPackage, &tp)
+	unpacked, err := generalDeleteAckPackageArgs.Unpack(serializedPackage)
 	if err != nil {
-		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "deserialize delete object syn package failed")
+		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "deserialize delete object ack package failed")
+	}
+
+	unpackedStruct := abi.ConvertType(unpacked[0], DeleteObjectAckPackage{})
+	tp, ok := unpackedStruct.(DeleteObjectAckPackage)
+	if !ok {
+		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "reflect delete object ack package failed")
 	}
 	return &tp, nil
 }
@@ -461,11 +777,22 @@ func (p DeleteGroupSynPackage) ValidateBasic() error {
 }
 
 func DeserializeDeleteGroupSynPackage(serializedPackage []byte) (interface{}, error) {
-	var tp DeleteGroupSynPackage
-	err := rlp.DecodeBytes(serializedPackage, &tp)
+	unpacked, err := generalDeleteSynPackageArgs.Unpack(serializedPackage)
 	if err != nil {
 		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "deserialize delete group syn package failed")
 	}
+
+	unpackedStruct := abi.ConvertType(unpacked[0], GeneralDeleteSynPackageStruct{})
+	pkgStruct, ok := unpackedStruct.(GeneralDeleteSynPackageStruct)
+	if !ok {
+		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "reflect delete group syn package failed")
+	}
+
+	tp := DeleteGroupSynPackage{
+		Operator:  pkgStruct.Operator.Bytes(),
+		Id:        pkgStruct.Id,
+		ExtraData: pkgStruct.ExtraData,
+	}
 	return &tp, nil
 }
 
@@ -476,16 +803,25 @@ type DeleteGroupAckPackage struct {
 }
 
 func DeserializeDeleteGroupAckPackage(serializedPackage []byte) (interface{}, error) {
-	var tp DeleteGroupAckPackage
-	err := rlp.DecodeBytes(serializedPackage, &tp)
+	unpacked, err := generalDeleteAckPackageArgs.Unpack(serializedPackage)
 	if err != nil {
 		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "deserialize delete group ack package failed")
 	}
+
+	unpackedStruct := abi.ConvertType(unpacked[0], DeleteGroupAckPackage{})
+	tp, ok := unpackedStruct.(DeleteGroupAckPackage)
+	if !ok {
+		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "reflect delete group ack package failed")
+	}
 	return &tp, nil
 }
 
 func (p DeleteGroupAckPackage) MustSerialize() []byte {
-	encodedBytes, err := rlp.EncodeToBytes(p)
+	encodedBytes, err := generalDeleteAckPackageArgs.Pack(&DeleteGroupAckPackage{
+		p.Status,
+		SafeBigInt(p.Id),
+		p.ExtraData,
+	})
 	if err != nil {
 		panic("encode delete group ack package error")
 	}
@@ -505,6 +841,28 @@ type UpdateGroupMemberSynPackage struct {
 	ExtraData     []byte
 }
 
+type UpdateGroupMemberSynPackageStruct struct {
+	Operator      common.Address
+	GroupId       *big.Int
+	OperationType uint8
+	Members       []common.Address
+	ExtraData     []byte
+}
+
+var (
+	updateGroupMemberSynPackageType, _ = abi.NewType("tuple", "", []abi.ArgumentMarshaling{
+		{Name: "Operator", Type: "address"},
+		{Name: "GroupId", Type: "uint256"},
+		{Name: "OperationType", Type: "uint8"},
+		{Name: "Members", Type: "address[]"},
+		{Name: "ExtraData", Type: "bytes"},
+	})
+
+	updateGroupMemberSynPackageArgs = abi.Arguments{
+		{Type: updateGroupMemberSynPackageType},
+	}
+)
+
 func (p UpdateGroupMemberSynPackage) GetMembers() []string {
 	members := make([]string, 0, len(p.Members))
 	for _, member := range p.Members {
@@ -534,10 +892,28 @@ func (p UpdateGroupMemberSynPackage) ValidateBasic() error {
 }
 
 func DeserializeUpdateGroupMemberSynPackage(serializedPackage []byte) (interface{}, error) {
-	var tp UpdateGroupMemberSynPackage
-	err := rlp.DecodeBytes(serializedPackage, &tp)
+	unpacked, err := updateGroupMemberSynPackageArgs.Unpack(serializedPackage)
 	if err != nil {
-		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "deserialize update group member syn package failed")
+		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "deserialize delete bucket ack package failed")
+	}
+
+	unpackedStruct := abi.ConvertType(unpacked[0], UpdateGroupMemberSynPackageStruct{})
+	pkgStruct, ok := unpackedStruct.(UpdateGroupMemberSynPackageStruct)
+	if !ok {
+		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "reflect delete bucket ack package failed")
+	}
+
+	totalMember := len(pkgStruct.Members)
+	members := make([]sdk.AccAddress, totalMember)
+	for i, member := range pkgStruct.Members {
+		members[i] = member.Bytes()
+	}
+	tp := UpdateGroupMemberSynPackage{
+		pkgStruct.Operator.Bytes(),
+		pkgStruct.GroupId,
+		pkgStruct.OperationType,
+		members,
+		pkgStruct.ExtraData,
 	}
 	return &tp, nil
 }
@@ -551,17 +927,73 @@ type UpdateGroupMemberAckPackage struct {
 	ExtraData     []byte
 }
 
+type UpdateGroupMemberAckPackageStruct struct {
+	Status        uint8
+	Id            *big.Int
+	Operator      common.Address
+	OperationType uint8
+	Members       []common.Address
+	ExtraData     []byte
+}
+
+var (
+	updateGroupMemberAckPackageType, _ = abi.NewType("tuple", "", []abi.ArgumentMarshaling{
+		{Name: "Status", Type: "uint8"},
+		{Name: "Id", Type: "uint256"},
+		{Name: "Operator", Type: "address"},
+		{Name: "OperationType", Type: "uint8"},
+		{Name: "Members", Type: "address[]"},
+		{Name: "ExtraData", Type: "bytes"},
+	})
+
+	updateGroupMemberAckPackageArgs = abi.Arguments{
+		{Type: updateGroupMemberAckPackageType},
+	}
+)
+
 func DeserializeUpdateGroupMemberAckPackage(serializedPackage []byte) (interface{}, error) {
-	var tp UpdateGroupMemberAckPackage
-	err := rlp.DecodeBytes(serializedPackage, &tp)
+	unpacked, err := updateGroupMemberAckPackageArgs.Unpack(serializedPackage)
 	if err != nil {
 		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "deserialize update group member ack package failed")
 	}
+
+	unpackedStruct := abi.ConvertType(unpacked[0], UpdateGroupMemberAckPackageStruct{})
+	pkgStruct, ok := unpackedStruct.(UpdateGroupMemberAckPackageStruct)
+	if !ok {
+		return nil, errors.Wrapf(ErrInvalidCrossChainPackage, "reflect update group member ack package failed")
+	}
+
+	totalMember := len(pkgStruct.Members)
+	members := make([]sdk.AccAddress, totalMember)
+	for i, member := range pkgStruct.Members {
+		members[i] = member.Bytes()
+	}
+	tp := UpdateGroupMemberAckPackage{
+		pkgStruct.Status,
+		pkgStruct.Id,
+		pkgStruct.Operator.Bytes(),
+		pkgStruct.OperationType,
+		members,
+		pkgStruct.ExtraData,
+	}
 	return &tp, nil
 }
 
 func (p UpdateGroupMemberAckPackage) MustSerialize() []byte {
-	encodedBytes, err := rlp.EncodeToBytes(p)
+	totalMember := len(p.Members)
+	members := make([]common.Address, totalMember)
+	for i, member := range p.Members {
+		members[i] = common.BytesToAddress(member)
+	}
+
+	encodedBytes, err := updateGroupMemberAckPackageArgs.Pack(&UpdateGroupMemberAckPackageStruct{
+		p.Status,
+		SafeBigInt(p.Id),
+		common.BytesToAddress(p.Operator),
+		p.OperationType,
+		members,
+		p.ExtraData,
+	})
 	if err != nil {
 		panic("encode delete group ack package error")
 	}
diff --git a/x/storage/types/errors.go b/x/storage/types/errors.go
index 69b99f8ac..efe59093c 100644
--- a/x/storage/types/errors.go
+++ b/x/storage/types/errors.go
@@ -2,6 +2,8 @@ package types
 
 import (
 	"cosmossdk.io/errors"
+
+	"github.com/bnb-chain/greenfield/x/virtualgroup/types"
 )
 
 // x/storage module sentinel errors
@@ -27,22 +29,25 @@ var (
 	ErrInvalidVisibility        = errors.Register(ModuleName, 1118, "Invalid type of visibility")
 	ErrUpdateQuotaFailed        = errors.Register(ModuleName, 1119, "Update quota failed")
 
-	ErrNoSuchPolicy          = errors.Register(ModuleName, 1120, "No such Policy")
-	ErrInvalidParameter      = errors.Register(ModuleName, 1121, "Invalid parameter")
-	ErrInvalidRedundancyType = errors.Register(ModuleName, 1122, "Invalid redundancy type")
+	ErrNoSuchPolicy = errors.Register(ModuleName, 1120, "No such Policy")
+
+	ErrInvalidRedundancyType     = errors.Register(ModuleName, 1122, "Invalid redundancy type")
+	ErrInvalidGlobalVirtualGroup = errors.Register(ModuleName, 1123, "invalid global virtual group")
 
 	ErrInvalidCrossChainPackage = errors.Register(ModuleName, 3000, "invalid cross chain package")
 	ErrAlreadyMirrored          = errors.Register(ModuleName, 3001, "resource is already mirrored")
 	ErrInvalidOperationType     = errors.Register(ModuleName, 3002, "invalid operation type")
 	ErrInvalidId                = errors.Register(ModuleName, 3003, "id is invalid")
 
-	ErrInvalidObjectIds    = errors.Register(ModuleName, 3101, "object ids are invalid")
-	ErrInvalidReason       = errors.Register(ModuleName, 3102, "reason is invalid")
-	ErrNoMoreDiscontinue   = errors.Register(ModuleName, 3103, "no more discontinue requests")
-	ErrBucketDiscontinued  = errors.Register(ModuleName, 3104, "the bucket is discontinued")
-	ErrInvalidObjectStatus = errors.Register(ModuleName, 3105, "invalid object status")
-	ErrInvalidBucketStatus = errors.Register(ModuleName, 3106, "invalid bucket status")
-
-	ErrKeyNotExist     = errors.Register(ModuleName, 3201, "DeletePermissionKey not exist")
-	ErrInvalidResource = errors.Register(ModuleName, 3202, "invalid resource type")
+	ErrInvalidObjectIds          = errors.Register(ModuleName, 3101, "object ids are invalid")
+	ErrInvalidReason             = errors.Register(ModuleName, 3102, "reason is invalid")
+	ErrNoMoreDiscontinue         = errors.Register(ModuleName, 3103, "no more discontinue requests")
+	ErrBucketDiscontinued        = errors.Register(ModuleName, 3104, "the bucket is discontinued")
+	ErrInvalidObjectStatus       = errors.Register(ModuleName, 3105, "invalid object status")
+	ErrInvalidBucketStatus       = errors.Register(ModuleName, 3106, "invalid bucket status")
+	ErrBucketMigrating           = errors.Register(ModuleName, 3107, "the bucket is migrating")
+	ErrInvalidResource           = errors.Register(ModuleName, 3201, "invalid resource type")
+	ErrMigrationBucketFailed     = errors.Register(ModuleName, 3202, "migrate bucket failed.")
+	ErrVirtualGroupOperateFailed = errors.Register(ModuleName, 3203, "operate virtual group failed.")
+	ErrInvalidBlsPubKey          = errors.Register(types.ModuleName, 1122, "invalid bls public key")
 )
diff --git a/x/storage/types/events.pb.go b/x/storage/types/events.pb.go
index b9d2fc917..aa8189302 100644
--- a/x/storage/types/events.pb.go
+++ b/x/storage/types/events.pb.go
@@ -42,10 +42,12 @@ type EventCreateBucket struct {
 	ChargedReadQuota uint64 `protobuf:"varint,7,opt,name=charged_read_quota,json=chargedReadQuota,proto3" json:"charged_read_quota,omitempty"`
 	// payment_address is the address of the payment account
 	PaymentAddress string `protobuf:"bytes,8,opt,name=payment_address,json=paymentAddress,proto3" json:"payment_address,omitempty"`
-	// primary_sp_address is the operator address of the primary sp.
-	PrimarySpAddress string `protobuf:"bytes,9,opt,name=primary_sp_address,json=primarySpAddress,proto3" json:"primary_sp_address,omitempty"`
+	// primary_sp_id is the unique id of primary sp.
+	PrimarySpId uint32 `protobuf:"varint,9,opt,name=primary_sp_id,json=primarySpId,proto3" json:"primary_sp_id,omitempty"`
+	// global_virtual_group_family_id defines the unique id of gvg family
+	GlobalVirtualGroupFamilyId uint32 `protobuf:"varint,10,opt,name=global_virtual_group_family_id,json=globalVirtualGroupFamilyId,proto3" json:"global_virtual_group_family_id,omitempty"`
 	// status define the status of the bucket.
-	Status BucketStatus `protobuf:"varint,10,opt,name=status,proto3,enum=greenfield.storage.BucketStatus" json:"status,omitempty"`
+	Status BucketStatus `protobuf:"varint,11,opt,name=status,proto3,enum=greenfield.storage.BucketStatus" json:"status,omitempty"`
 }
 
 func (m *EventCreateBucket) Reset()         { *m = EventCreateBucket{} }
@@ -130,11 +132,18 @@ func (m *EventCreateBucket) GetPaymentAddress() string {
 	return ""
 }
 
-func (m *EventCreateBucket) GetPrimarySpAddress() string {
+func (m *EventCreateBucket) GetPrimarySpId() uint32 {
 	if m != nil {
-		return m.PrimarySpAddress
+		return m.PrimarySpId
 	}
-	return ""
+	return 0
+}
+
+func (m *EventCreateBucket) GetGlobalVirtualGroupFamilyId() uint32 {
+	if m != nil {
+		return m.GlobalVirtualGroupFamilyId
+	}
+	return 0
 }
 
 func (m *EventCreateBucket) GetStatus() BucketStatus {
@@ -154,8 +163,8 @@ type EventDeleteBucket struct {
 	BucketName string `protobuf:"bytes,3,opt,name=bucket_name,json=bucketName,proto3" json:"bucket_name,omitempty"`
 	// bucket_id define an u256 id for bucket
 	BucketId Uint `protobuf:"bytes,4,opt,name=bucket_id,json=bucketId,proto3,customtype=Uint" json:"bucket_id"`
-	// primary_sp_address define the account address of primary sp
-	PrimarySpAddress string `protobuf:"bytes,5,opt,name=primary_sp_address,json=primarySpAddress,proto3" json:"primary_sp_address,omitempty"`
+	// primary_sp_id is the unique id of primary sp.
+	PrimarySpId uint32 `protobuf:"varint,5,opt,name=primary_sp_id,json=primarySpId,proto3" json:"primary_sp_id,omitempty"`
 }
 
 func (m *EventDeleteBucket) Reset()         { *m = EventDeleteBucket{} }
@@ -212,11 +221,11 @@ func (m *EventDeleteBucket) GetBucketName() string {
 	return ""
 }
 
-func (m *EventDeleteBucket) GetPrimarySpAddress() string {
+func (m *EventDeleteBucket) GetPrimarySpId() uint32 {
 	if m != nil {
-		return m.PrimarySpAddress
+		return m.PrimarySpId
 	}
-	return ""
+	return 0
 }
 
 // EventUpdateBucketInfo is emitted on MsgUpdateBucketInfo
@@ -401,8 +410,8 @@ type EventCreateObject struct {
 	BucketId Uint `protobuf:"bytes,6,opt,name=bucket_id,json=bucketId,proto3,customtype=Uint" json:"bucket_id"`
 	// object_id define an u256 id for object
 	ObjectId Uint `protobuf:"bytes,7,opt,name=object_id,json=objectId,proto3,customtype=Uint" json:"object_id"`
-	// primary_sp_address define the account address of primary sp
-	PrimarySpAddress string `protobuf:"bytes,8,opt,name=primary_sp_address,json=primarySpAddress,proto3" json:"primary_sp_address,omitempty"`
+	// primary_sp_id define the unique id of primary sp
+	PrimarySpId uint32 `protobuf:"varint,8,opt,name=primary_sp_id,json=primarySpId,proto3" json:"primary_sp_id,omitempty"`
 	// payload_size define the size of payload data which you want upload
 	PayloadSize uint64 `protobuf:"varint,9,opt,name=payload_size,json=payloadSize,proto3" json:"payload_size,omitempty"`
 	// visibility defines the highest permission of object.
@@ -482,11 +491,11 @@ func (m *EventCreateObject) GetObjectName() string {
 	return ""
 }
 
-func (m *EventCreateObject) GetPrimarySpAddress() string {
+func (m *EventCreateObject) GetPrimarySpId() uint32 {
 	if m != nil {
-		return m.PrimarySpAddress
+		return m.PrimarySpId
 	}
-	return ""
+	return 0
 }
 
 func (m *EventCreateObject) GetPayloadSize() uint64 {
@@ -545,7 +554,7 @@ func (m *EventCreateObject) GetChecksums() [][]byte {
 	return nil
 }
 
-// EventSealObject is emitted on MsgSealObject
+// EventCancelCreateObject is emitted on MsgCancelCreateObject
 type EventCancelCreateObject struct {
 	// operator define the account address of operator who cancel create object
 	Operator string `protobuf:"bytes,1,opt,name=operator,proto3" json:"operator,omitempty"`
@@ -553,8 +562,8 @@ type EventCancelCreateObject struct {
 	BucketName string `protobuf:"bytes,2,opt,name=bucket_name,json=bucketName,proto3" json:"bucket_name,omitempty"`
 	// object_name define the name of the object
 	ObjectName string `protobuf:"bytes,3,opt,name=object_name,json=objectName,proto3" json:"object_name,omitempty"`
-	// primary_sp_address define the operator account address of the sp
-	PrimarySpAddress string `protobuf:"bytes,4,opt,name=primary_sp_address,json=primarySpAddress,proto3" json:"primary_sp_address,omitempty"`
+	// primary_sp_id define the unique id of primary sp
+	PrimarySpId uint32 `protobuf:"varint,4,opt,name=primary_sp_id,json=primarySpId,proto3" json:"primary_sp_id,omitempty"`
 	// id define an u256 id for object
 	ObjectId Uint `protobuf:"bytes,6,opt,name=object_id,json=objectId,proto3,customtype=Uint" json:"object_id"`
 }
@@ -613,11 +622,11 @@ func (m *EventCancelCreateObject) GetObjectName() string {
 	return ""
 }
 
-func (m *EventCancelCreateObject) GetPrimarySpAddress() string {
+func (m *EventCancelCreateObject) GetPrimarySpId() uint32 {
 	if m != nil {
-		return m.PrimarySpAddress
+		return m.PrimarySpId
 	}
-	return ""
+	return 0
 }
 
 // EventSealObject is emitted on MsgSealObject
@@ -632,8 +641,10 @@ type EventSealObject struct {
 	ObjectId Uint `protobuf:"bytes,5,opt,name=object_id,json=objectId,proto3,customtype=Uint" json:"object_id"`
 	// status define the status of the object. INIT or IN_SERVICE or others
 	Status ObjectStatus `protobuf:"varint,6,opt,name=status,proto3,enum=greenfield.storage.ObjectStatus" json:"status,omitempty"`
-	// secondary_sp_address define all the operator address of the secondary sps
-	SecondarySpAddresses []string `protobuf:"bytes,7,rep,name=secondary_sp_addresses,json=secondarySpAddresses,proto3" json:"secondary_sp_addresses,omitempty"`
+	// global_virtual_group_id defines the unique id of gvg which the object stored
+	GlobalVirtualGroupId uint32 `protobuf:"varint,7,opt,name=global_virtual_group_id,json=globalVirtualGroupId,proto3" json:"global_virtual_group_id,omitempty"`
+	// local_virtual_group_id defines the unique id of lvg which the object stored
+	LocalVirtualGroupId uint32 `protobuf:"varint,8,opt,name=local_virtual_group_id,json=localVirtualGroupId,proto3" json:"local_virtual_group_id,omitempty"`
 }
 
 func (m *EventSealObject) Reset()         { *m = EventSealObject{} }
@@ -697,11 +708,18 @@ func (m *EventSealObject) GetStatus() ObjectStatus {
 	return OBJECT_STATUS_CREATED
 }
 
-func (m *EventSealObject) GetSecondarySpAddresses() []string {
+func (m *EventSealObject) GetGlobalVirtualGroupId() uint32 {
 	if m != nil {
-		return m.SecondarySpAddresses
+		return m.GlobalVirtualGroupId
 	}
-	return nil
+	return 0
+}
+
+func (m *EventSealObject) GetLocalVirtualGroupId() uint32 {
+	if m != nil {
+		return m.LocalVirtualGroupId
+	}
+	return 0
 }
 
 // EventCopyObject is emitted on MsgCopyObject
@@ -800,10 +818,8 @@ type EventDeleteObject struct {
 	ObjectName string `protobuf:"bytes,3,opt,name=object_name,json=objectName,proto3" json:"object_name,omitempty"`
 	// id define an u256 id for object
 	ObjectId Uint `protobuf:"bytes,4,opt,name=object_id,json=objectId,proto3,customtype=Uint" json:"object_id"`
-	// primary_sp_address define the operator account address of the sp
-	PrimarySpAddress string `protobuf:"bytes,5,opt,name=primary_sp_address,json=primarySpAddress,proto3" json:"primary_sp_address,omitempty"`
-	// secondary_sp_address define all the operator address of the secondary sps
-	SecondarySpAddresses []string `protobuf:"bytes,6,rep,name=secondary_sp_addresses,json=secondarySpAddresses,proto3" json:"secondary_sp_addresses,omitempty"`
+	// local_virtual_group_id defines the unique id of lvg which the object stored
+	LocalVirtualGroupId uint32 `protobuf:"varint,5,opt,name=local_virtual_group_id,json=localVirtualGroupId,proto3" json:"local_virtual_group_id,omitempty"`
 }
 
 func (m *EventDeleteObject) Reset()         { *m = EventDeleteObject{} }
@@ -860,18 +876,11 @@ func (m *EventDeleteObject) GetObjectName() string {
 	return ""
 }
 
-func (m *EventDeleteObject) GetPrimarySpAddress() string {
-	if m != nil {
-		return m.PrimarySpAddress
-	}
-	return ""
-}
-
-func (m *EventDeleteObject) GetSecondarySpAddresses() []string {
+func (m *EventDeleteObject) GetLocalVirtualGroupId() uint32 {
 	if m != nil {
-		return m.SecondarySpAddresses
+		return m.LocalVirtualGroupId
 	}
-	return nil
+	return 0
 }
 
 // EventRejectSealObject is emitted on MsgRejectSealObject
@@ -1455,6 +1464,8 @@ type EventMirrorBucket struct {
 	BucketName string `protobuf:"bytes,2,opt,name=bucket_name,json=bucketName,proto3" json:"bucket_name,omitempty"`
 	// bucket_id define an u256 id for bucket
 	BucketId Uint `protobuf:"bytes,4,opt,name=bucket_id,json=bucketId,proto3,customtype=Uint" json:"bucket_id"`
+	// chain id of the destination chain
+	DestChainId uint32 `protobuf:"varint,5,opt,name=dest_chain_id,json=destChainId,proto3" json:"dest_chain_id,omitempty"`
 }
 
 func (m *EventMirrorBucket) Reset()         { *m = EventMirrorBucket{} }
@@ -1504,6 +1515,13 @@ func (m *EventMirrorBucket) GetBucketName() string {
 	return ""
 }
 
+func (m *EventMirrorBucket) GetDestChainId() uint32 {
+	if m != nil {
+		return m.DestChainId
+	}
+	return 0
+}
+
 // EventMirrorBucketResult is emitted on receiving ack package from destination chain
 type EventMirrorBucketResult struct {
 	// status define the status of the result
@@ -1512,6 +1530,8 @@ type EventMirrorBucketResult struct {
 	BucketName string `protobuf:"bytes,2,opt,name=bucket_name,json=bucketName,proto3" json:"bucket_name,omitempty"`
 	// bucket_id define an u256 id for bucket
 	BucketId Uint `protobuf:"bytes,4,opt,name=bucket_id,json=bucketId,proto3,customtype=Uint" json:"bucket_id"`
+	// chain id of the destination chain
+	DestChainId uint32 `protobuf:"varint,5,opt,name=dest_chain_id,json=destChainId,proto3" json:"dest_chain_id,omitempty"`
 }
 
 func (m *EventMirrorBucketResult) Reset()         { *m = EventMirrorBucketResult{} }
@@ -1561,6 +1581,13 @@ func (m *EventMirrorBucketResult) GetBucketName() string {
 	return ""
 }
 
+func (m *EventMirrorBucketResult) GetDestChainId() uint32 {
+	if m != nil {
+		return m.DestChainId
+	}
+	return 0
+}
+
 // EventMirrorObject is emitted on MirrorObject
 type EventMirrorObject struct {
 	// operator define the account address of operator who delete the object
@@ -1571,6 +1598,8 @@ type EventMirrorObject struct {
 	ObjectName string `protobuf:"bytes,3,opt,name=object_name,json=objectName,proto3" json:"object_name,omitempty"`
 	// object_id define an u256 id for object
 	ObjectId Uint `protobuf:"bytes,4,opt,name=object_id,json=objectId,proto3,customtype=Uint" json:"object_id"`
+	// chain id of the destination chain
+	DestChainId uint32 `protobuf:"varint,5,opt,name=dest_chain_id,json=destChainId,proto3" json:"dest_chain_id,omitempty"`
 }
 
 func (m *EventMirrorObject) Reset()         { *m = EventMirrorObject{} }
@@ -1627,6 +1656,13 @@ func (m *EventMirrorObject) GetObjectName() string {
 	return ""
 }
 
+func (m *EventMirrorObject) GetDestChainId() uint32 {
+	if m != nil {
+		return m.DestChainId
+	}
+	return 0
+}
+
 // EventMirrorObjectResult is emitted on receiving ack package from destination chain
 type EventMirrorObjectResult struct {
 	// status define the status of the result
@@ -1637,6 +1673,8 @@ type EventMirrorObjectResult struct {
 	ObjectName string `protobuf:"bytes,3,opt,name=object_name,json=objectName,proto3" json:"object_name,omitempty"`
 	// object_id define an u256 id for object
 	ObjectId Uint `protobuf:"bytes,4,opt,name=object_id,json=objectId,proto3,customtype=Uint" json:"object_id"`
+	// chain id of the destination chain
+	DestChainId uint32 `protobuf:"varint,5,opt,name=dest_chain_id,json=destChainId,proto3" json:"dest_chain_id,omitempty"`
 }
 
 func (m *EventMirrorObjectResult) Reset()         { *m = EventMirrorObjectResult{} }
@@ -1693,6 +1731,13 @@ func (m *EventMirrorObjectResult) GetObjectName() string {
 	return ""
 }
 
+func (m *EventMirrorObjectResult) GetDestChainId() uint32 {
+	if m != nil {
+		return m.DestChainId
+	}
+	return 0
+}
+
 // EventMirrorGroup is emitted on MirrorGroup
 type EventMirrorGroup struct {
 	// owner define the account address of group owner
@@ -1701,6 +1746,8 @@ type EventMirrorGroup struct {
 	GroupName string `protobuf:"bytes,2,opt,name=group_name,json=groupName,proto3" json:"group_name,omitempty"`
 	// group_id define an u256 id for group
 	GroupId Uint `protobuf:"bytes,3,opt,name=group_id,json=groupId,proto3,customtype=Uint" json:"group_id"`
+	// chain id of the destination chain
+	DestChainId uint32 `protobuf:"varint,4,opt,name=dest_chain_id,json=destChainId,proto3" json:"dest_chain_id,omitempty"`
 }
 
 func (m *EventMirrorGroup) Reset()         { *m = EventMirrorGroup{} }
@@ -1750,6 +1797,13 @@ func (m *EventMirrorGroup) GetGroupName() string {
 	return ""
 }
 
+func (m *EventMirrorGroup) GetDestChainId() uint32 {
+	if m != nil {
+		return m.DestChainId
+	}
+	return 0
+}
+
 // EventMirrorGroupResult is emitted on receiving ack package from destination chain
 type EventMirrorGroupResult struct {
 	// status define the status of the result
@@ -1758,6 +1812,8 @@ type EventMirrorGroupResult struct {
 	GroupName string `protobuf:"bytes,2,opt,name=group_name,json=groupName,proto3" json:"group_name,omitempty"`
 	// group_id define an u256 id for group
 	GroupId Uint `protobuf:"bytes,3,opt,name=group_id,json=groupId,proto3,customtype=Uint" json:"group_id"`
+	// chain id of the destination chain
+	DestChainId uint32 `protobuf:"varint,4,opt,name=dest_chain_id,json=destChainId,proto3" json:"dest_chain_id,omitempty"`
 }
 
 func (m *EventMirrorGroupResult) Reset()         { *m = EventMirrorGroupResult{} }
@@ -1807,6 +1863,13 @@ func (m *EventMirrorGroupResult) GetGroupName() string {
 	return ""
 }
 
+func (m *EventMirrorGroupResult) GetDestChainId() uint32 {
+	if m != nil {
+		return m.DestChainId
+	}
+	return 0
+}
+
 // EventStalePolicyCleanup is emitted when specified block height's stale policies need to be Garbage collected
 type EventStalePolicyCleanup struct {
 	BlockNum   int64       `protobuf:"varint,1,opt,name=blockNum,proto3" json:"blockNum,omitempty"`
@@ -1860,6 +1923,204 @@ func (m *EventStalePolicyCleanup) GetDeleteInfo() *DeleteInfo {
 	return nil
 }
 
+type EventMigrationBucket struct {
+	// The address of the operator that initiated the bucket migration,
+	// usually the owner of the bucket or another account which has permission to operate
+	Operator string `protobuf:"bytes,1,opt,name=operator,proto3" json:"operator,omitempty"`
+	// The name of the bucket to be migrated
+	BucketName string `protobuf:"bytes,2,opt,name=bucket_name,json=bucketName,proto3" json:"bucket_name,omitempty"`
+	// bucket_id define an u256 id for object
+	BucketId Uint `protobuf:"bytes,3,opt,name=bucket_id,json=bucketId,proto3,customtype=Uint" json:"bucket_id"`
+	// The id of the destination primary sp
+	DstPrimarySpId uint32 `protobuf:"varint,4,opt,name=dst_primary_sp_id,json=dstPrimarySpId,proto3" json:"dst_primary_sp_id,omitempty"`
+}
+
+func (m *EventMigrationBucket) Reset()         { *m = EventMigrationBucket{} }
+func (m *EventMigrationBucket) String() string { return proto.CompactTextString(m) }
+func (*EventMigrationBucket) ProtoMessage()    {}
+func (*EventMigrationBucket) Descriptor() ([]byte, []int) {
+	return fileDescriptor_946dcba4f763ddc4, []int{24}
+}
+func (m *EventMigrationBucket) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *EventMigrationBucket) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_EventMigrationBucket.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *EventMigrationBucket) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_EventMigrationBucket.Merge(m, src)
+}
+func (m *EventMigrationBucket) XXX_Size() int {
+	return m.Size()
+}
+func (m *EventMigrationBucket) XXX_DiscardUnknown() {
+	xxx_messageInfo_EventMigrationBucket.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_EventMigrationBucket proto.InternalMessageInfo
+
+func (m *EventMigrationBucket) GetOperator() string {
+	if m != nil {
+		return m.Operator
+	}
+	return ""
+}
+
+func (m *EventMigrationBucket) GetBucketName() string {
+	if m != nil {
+		return m.BucketName
+	}
+	return ""
+}
+
+func (m *EventMigrationBucket) GetDstPrimarySpId() uint32 {
+	if m != nil {
+		return m.DstPrimarySpId
+	}
+	return 0
+}
+
+type EventCancelMigrationBucket struct {
+	// The address of the operator that canceled the bucket migration,
+	// usually the owner of the bucket or another account which has permission to operate
+	Operator string `protobuf:"bytes,1,opt,name=operator,proto3" json:"operator,omitempty"`
+	// The name of the bucket to be migrated
+	BucketName string `protobuf:"bytes,2,opt,name=bucket_name,json=bucketName,proto3" json:"bucket_name,omitempty"`
+	// bucket_id define an u256 id for object
+	BucketId Uint `protobuf:"bytes,3,opt,name=bucket_id,json=bucketId,proto3,customtype=Uint" json:"bucket_id"`
+}
+
+func (m *EventCancelMigrationBucket) Reset()         { *m = EventCancelMigrationBucket{} }
+func (m *EventCancelMigrationBucket) String() string { return proto.CompactTextString(m) }
+func (*EventCancelMigrationBucket) ProtoMessage()    {}
+func (*EventCancelMigrationBucket) Descriptor() ([]byte, []int) {
+	return fileDescriptor_946dcba4f763ddc4, []int{25}
+}
+func (m *EventCancelMigrationBucket) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *EventCancelMigrationBucket) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_EventCancelMigrationBucket.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *EventCancelMigrationBucket) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_EventCancelMigrationBucket.Merge(m, src)
+}
+func (m *EventCancelMigrationBucket) XXX_Size() int {
+	return m.Size()
+}
+func (m *EventCancelMigrationBucket) XXX_DiscardUnknown() {
+	xxx_messageInfo_EventCancelMigrationBucket.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_EventCancelMigrationBucket proto.InternalMessageInfo
+
+func (m *EventCancelMigrationBucket) GetOperator() string {
+	if m != nil {
+		return m.Operator
+	}
+	return ""
+}
+
+func (m *EventCancelMigrationBucket) GetBucketName() string {
+	if m != nil {
+		return m.BucketName
+	}
+	return ""
+}
+
+type EventCompleteMigrationBucket struct {
+	// The address of the operator that initiated the bucket migration,
+	// usually the owner of the bucket or another account which has permission to operate
+	Operator string `protobuf:"bytes,1,opt,name=operator,proto3" json:"operator,omitempty"`
+	// The name of the bucket to be migrated
+	BucketName string `protobuf:"bytes,2,opt,name=bucket_name,json=bucketName,proto3" json:"bucket_name,omitempty"`
+	// bucket_id define an u256 id for object
+	BucketId Uint `protobuf:"bytes,3,opt,name=bucket_id,json=bucketId,proto3,customtype=Uint" json:"bucket_id"`
+	// The family id that the bucket to be migrated to
+	GlobalVirtualGroupFamilyId uint32 `protobuf:"varint,4,opt,name=global_virtual_group_family_id,json=globalVirtualGroupFamilyId,proto3" json:"global_virtual_group_family_id,omitempty"`
+	// The src and dst gvg mapping
+	GvgMappings []*GVGMapping `protobuf:"bytes,5,rep,name=gvg_mappings,json=gvgMappings,proto3" json:"gvg_mappings,omitempty"`
+}
+
+func (m *EventCompleteMigrationBucket) Reset()         { *m = EventCompleteMigrationBucket{} }
+func (m *EventCompleteMigrationBucket) String() string { return proto.CompactTextString(m) }
+func (*EventCompleteMigrationBucket) ProtoMessage()    {}
+func (*EventCompleteMigrationBucket) Descriptor() ([]byte, []int) {
+	return fileDescriptor_946dcba4f763ddc4, []int{26}
+}
+func (m *EventCompleteMigrationBucket) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *EventCompleteMigrationBucket) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_EventCompleteMigrationBucket.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *EventCompleteMigrationBucket) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_EventCompleteMigrationBucket.Merge(m, src)
+}
+func (m *EventCompleteMigrationBucket) XXX_Size() int {
+	return m.Size()
+}
+func (m *EventCompleteMigrationBucket) XXX_DiscardUnknown() {
+	xxx_messageInfo_EventCompleteMigrationBucket.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_EventCompleteMigrationBucket proto.InternalMessageInfo
+
+func (m *EventCompleteMigrationBucket) GetOperator() string {
+	if m != nil {
+		return m.Operator
+	}
+	return ""
+}
+
+func (m *EventCompleteMigrationBucket) GetBucketName() string {
+	if m != nil {
+		return m.BucketName
+	}
+	return ""
+}
+
+func (m *EventCompleteMigrationBucket) GetGlobalVirtualGroupFamilyId() uint32 {
+	if m != nil {
+		return m.GlobalVirtualGroupFamilyId
+	}
+	return 0
+}
+
+func (m *EventCompleteMigrationBucket) GetGvgMappings() []*GVGMapping {
+	if m != nil {
+		return m.GvgMappings
+	}
+	return nil
+}
+
 func init() {
 	proto.RegisterType((*EventCreateBucket)(nil), "greenfield.storage.EventCreateBucket")
 	proto.RegisterType((*EventDeleteBucket)(nil), "greenfield.storage.EventDeleteBucket")
@@ -1885,101 +2146,115 @@ func init() {
 	proto.RegisterType((*EventMirrorGroup)(nil), "greenfield.storage.EventMirrorGroup")
 	proto.RegisterType((*EventMirrorGroupResult)(nil), "greenfield.storage.EventMirrorGroupResult")
 	proto.RegisterType((*EventStalePolicyCleanup)(nil), "greenfield.storage.EventStalePolicyCleanup")
+	proto.RegisterType((*EventMigrationBucket)(nil), "greenfield.storage.EventMigrationBucket")
+	proto.RegisterType((*EventCancelMigrationBucket)(nil), "greenfield.storage.EventCancelMigrationBucket")
+	proto.RegisterType((*EventCompleteMigrationBucket)(nil), "greenfield.storage.EventCompleteMigrationBucket")
 }
 
 func init() { proto.RegisterFile("greenfield/storage/events.proto", fileDescriptor_946dcba4f763ddc4) }
 
 var fileDescriptor_946dcba4f763ddc4 = []byte{
-	// 1423 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x59, 0xcf, 0x8f, 0xdb, 0x44,
-	0x14, 0x5e, 0x27, 0x4e, 0x36, 0x99, 0x6c, 0x36, 0x5b, 0xb3, 0x5d, 0xdc, 0x16, 0xb2, 0xa9, 0x0f,
-	0xd5, 0x1e, 0xe8, 0xae, 0xb4, 0x54, 0x50, 0x2e, 0x54, 0x49, 0x5b, 0xd0, 0x0a, 0xda, 0x82, 0xd3,
-	0x72, 0xe0, 0x62, 0x4d, 0xec, 0xd9, 0xac, 0x69, 0xec, 0x31, 0x33, 0xe3, 0xa5, 0xe9, 0x91, 0x0b,
-	0x17, 0x90, 0x90, 0x38, 0x21, 0x71, 0xec, 0x81, 0x0b, 0x27, 0xfa, 0x2f, 0x20, 0x15, 0x24, 0xa4,
-	0xaa, 0x27, 0x7e, 0x88, 0x0a, 0xb5, 0x27, 0xf8, 0x2b, 0x90, 0x67, 0x26, 0x8e, 0x9d, 0xa4, 0x9b,
-	0x75, 0x96, 0xc0, 0xf6, 0x16, 0x8f, 0xbf, 0x37, 0xf3, 0xde, 0xe7, 0xef, 0xfd, 0xb0, 0x03, 0xd6,
-	0xbb, 0x04, 0x21, 0x7f, 0xd7, 0x45, 0x3d, 0x67, 0x8b, 0x32, 0x4c, 0x60, 0x17, 0x6d, 0xa1, 0x7d,
-	0xe4, 0x33, 0xba, 0x19, 0x10, 0xcc, 0xb0, 0xa6, 0x0d, 0x01, 0x9b, 0x12, 0x70, 0xfa, 0x94, 0x8d,
-	0xa9, 0x87, 0xa9, 0xc5, 0x11, 0x5b, 0xe2, 0x42, 0xc0, 0x4f, 0xaf, 0x76, 0x71, 0x17, 0x8b, 0xf5,
-	0xe8, 0x97, 0x5c, 0x9d, 0x74, 0x8a, 0x8d, 0x3d, 0x0f, 0xfb, 0x12, 0x50, 0x9f, 0x00, 0x60, 0xfd,
-	0x00, 0xc9, 0x6d, 0x8d, 0xef, 0x55, 0x70, 0xe2, 0x6a, 0xe4, 0xd6, 0x65, 0x82, 0x20, 0x43, 0xad,
-	0xd0, 0xbe, 0x8d, 0x98, 0xb6, 0x09, 0x0a, 0xf8, 0x13, 0x1f, 0x11, 0x5d, 0x69, 0x28, 0x1b, 0xe5,
-	0x96, 0xfe, 0xe8, 0xfe, 0xf9, 0x55, 0xe9, 0x4d, 0xd3, 0x71, 0x08, 0xa2, 0xb4, 0xcd, 0x88, 0xeb,
-	0x77, 0x4d, 0x01, 0xd3, 0xd6, 0x41, 0xa5, 0xc3, 0x2d, 0x2d, 0x1f, 0x7a, 0x48, 0xcf, 0x45, 0x56,
-	0x26, 0x10, 0x4b, 0xd7, 0xa1, 0x87, 0xb4, 0x16, 0x00, 0xfb, 0x2e, 0x75, 0x3b, 0x6e, 0xcf, 0x65,
-	0x7d, 0x3d, 0xdf, 0x50, 0x36, 0x96, 0xb7, 0x8d, 0xcd, 0x71, 0x06, 0x36, 0x3f, 0x88, 0x51, 0x37,
-	0xfb, 0x01, 0x32, 0x13, 0x56, 0xda, 0x19, 0x50, 0xb6, 0xb9, 0x93, 0x16, 0x64, 0xba, 0xda, 0x50,
-	0x36, 0xf2, 0x66, 0x49, 0x2c, 0x34, 0x99, 0x76, 0x11, 0x94, 0xa5, 0x07, 0xae, 0xa3, 0x17, 0xb8,
-	0xd7, 0x67, 0x1e, 0x3c, 0x5e, 0x5f, 0xf8, 0xed, 0xf1, 0xba, 0x7a, 0xcb, 0xf5, 0xd9, 0xa3, 0xfb,
-	0xe7, 0x2b, 0x32, 0x82, 0xe8, 0xd2, 0x2c, 0x09, 0xf4, 0x8e, 0xa3, 0x5d, 0x02, 0x15, 0x8a, 0x43,
-	0x62, 0x23, 0x2b, 0xe2, 0x45, 0x2f, 0x72, 0xdf, 0xea, 0x93, 0x7c, 0x6b, 0x73, 0x98, 0xf0, 0x8b,
-	0xc6, 0xbf, 0xb5, 0x57, 0x80, 0x66, 0xef, 0x41, 0xd2, 0x45, 0x8e, 0x45, 0x10, 0x74, 0xac, 0x8f,
-	0x43, 0xcc, 0xa0, 0xbe, 0xd8, 0x50, 0x36, 0x54, 0x73, 0x45, 0xde, 0x31, 0x11, 0x74, 0xde, 0x8f,
-	0xd6, 0xb5, 0x26, 0xa8, 0x05, 0xb0, 0xef, 0x21, 0x9f, 0x59, 0x50, 0x50, 0xa9, 0x97, 0xa6, 0x90,
-	0xbc, 0x2c, 0x0d, 0xe4, 0xaa, 0xf6, 0x16, 0xd0, 0x02, 0xe2, 0x7a, 0x90, 0xf4, 0x2d, 0x1a, 0xc4,
-	0xbb, 0x94, 0xa7, 0xec, 0xb2, 0x22, 0x6d, 0xda, 0xc1, 0x60, 0x9f, 0x8b, 0xa0, 0x48, 0x19, 0x64,
-	0x21, 0xd5, 0x01, 0x0f, 0xba, 0x31, 0x29, 0x68, 0xa1, 0x88, 0x36, 0xc7, 0x99, 0x12, 0x6f, 0x7c,
-	0x93, 0x93, 0xaa, 0xb9, 0x82, 0x7a, 0x28, 0x56, 0xcd, 0x05, 0x50, 0xc2, 0x01, 0x22, 0x90, 0xe1,
-	0xe9, 0xc2, 0x89, 0x91, 0x43, 0xad, 0xe5, 0x66, 0xd2, 0x5a, 0x7e, 0x4c, 0x6b, 0x29, 0x29, 0xa8,
-	0x59, 0xa4, 0x30, 0x99, 0xd8, 0x42, 0x56, 0x62, 0x8d, 0x9f, 0xf2, 0xe0, 0x24, 0xa7, 0xe7, 0x56,
-	0xe0, 0xc4, 0x49, 0xb5, 0xe3, 0xef, 0xe2, 0x19, 0x29, 0x9a, 0x9a, 0x5e, 0xa9, 0x90, 0xf3, 0x59,
-	0x42, 0x7e, 0x03, 0x9c, 0x1a, 0x17, 0xaf, 0xd5, 0x41, 0xbb, 0x98, 0x20, 0x4e, 0x9e, 0x6a, 0xae,
-	0x8d, 0x6a, 0xb8, 0xc5, 0xef, 0x6a, 0xaf, 0x03, 0x7d, 0x82, 0x29, 0xdc, 0x65, 0x88, 0x70, 0xce,
-	0x54, 0xf3, 0xe4, 0xa8, 0x65, 0x33, 0xba, 0xa9, 0x5d, 0x00, 0x6b, 0x23, 0x29, 0x30, 0x38, 0xb0,
-	0xc8, 0x23, 0x5b, 0x4d, 0xeb, 0x5d, 0x1e, 0xb7, 0x0d, 0x4e, 0x8e, 0x5a, 0x89, 0xb3, 0x16, 0xb9,
-	0xd1, 0x0b, 0x69, 0x23, 0x71, 0x52, 0xba, 0xec, 0x94, 0x66, 0x29, 0x3b, 0xc6, 0xb7, 0x0a, 0x58,
-	0x13, 0x5a, 0x77, 0xa9, 0x8d, 0x7d, 0xe6, 0xfa, 0xe1, 0x40, 0xf0, 0x29, 0xda, 0x95, 0x2c, 0xb4,
-	0x4f, 0x7d, 0xa2, 0x6b, 0xa0, 0x48, 0x10, 0xa4, 0xd8, 0x97, 0x02, 0x97, 0x57, 0x51, 0x11, 0x74,
-	0x78, 0xce, 0x25, 0x8a, 0xa0, 0x58, 0x68, 0x32, 0xe3, 0xd3, 0x62, 0xaa, 0x98, 0xdf, 0xe8, 0x7c,
-	0x84, 0x6c, 0xa6, 0x6d, 0x83, 0x45, 0x5e, 0x26, 0x0f, 0x21, 0xb9, 0x01, 0xf0, 0xdf, 0x4f, 0xca,
-	0x75, 0x50, 0xc1, 0xdc, 0x1d, 0x01, 0x50, 0x05, 0x40, 0x2c, 0x8d, 0x4b, 0xb8, 0x98, 0x85, 0xcb,
-	0x8b, 0xa0, 0x2c, 0xb7, 0x76, 0x1d, 0x21, 0x86, 0x29, 0x96, 0x02, 0xfd, 0xcc, 0x7c, 0x2f, 0x65,
-	0x2e, 0xa4, 0x67, 0xc1, 0x52, 0x00, 0xfb, 0x3d, 0x0c, 0x1d, 0x8b, 0xba, 0x77, 0x11, 0x2f, 0xc5,
-	0xaa, 0x59, 0x91, 0x6b, 0x6d, 0xf7, 0xee, 0x68, 0x03, 0x04, 0x33, 0x35, 0xc0, 0xb3, 0x60, 0x29,
-	0x12, 0x60, 0x94, 0x01, 0xbc, 0x55, 0x55, 0x38, 0x89, 0x15, 0xb9, 0xc6, 0x7b, 0x51, 0xaa, 0x47,
-	0x2e, 0x8d, 0xf5, 0xc8, 0x41, 0xbd, 0xaf, 0x3e, 0xbb, 0xde, 0x0b, 0xd1, 0xa4, 0xeb, 0xbd, 0xf6,
-	0x0e, 0xa8, 0x11, 0xe4, 0x84, 0xbe, 0x03, 0x7d, 0xbb, 0x2f, 0x0e, 0x5f, 0x7e, 0x76, 0x08, 0x66,
-	0x0c, 0xe5, 0x21, 0x2c, 0x93, 0xd4, 0xf5, 0x68, 0xc3, 0xad, 0x65, 0x6e, 0xb8, 0x2f, 0x81, 0xb2,
-	0xbd, 0x87, 0xec, 0xdb, 0x34, 0xf4, 0xa8, 0xbe, 0xd2, 0xc8, 0x6f, 0x2c, 0x99, 0xc3, 0x05, 0xe3,
-	0xab, 0x1c, 0x78, 0x51, 0x24, 0x01, 0xf4, 0x6d, 0xd4, 0x4b, 0xa5, 0xc2, 0x9c, 0xca, 0xef, 0x88,
-	0xb8, 0xf3, 0x63, 0xe2, 0x9e, 0x2c, 0x34, 0x75, 0x86, 0x8e, 0x9d, 0x90, 0x7a, 0x31, 0x83, 0xd4,
-	0x8d, 0x9f, 0x73, 0xa0, 0xc6, 0x59, 0x69, 0x23, 0xd8, 0xfb, 0x9f, 0xd9, 0x48, 0x45, 0x51, 0xc8,
-	0x92, 0xb0, 0x43, 0x05, 0x17, 0x33, 0x2a, 0xf8, 0x3a, 0x58, 0xa3, 0xc8, 0xc6, 0xbe, 0x93, 0x7e,
-	0x06, 0x88, 0xea, 0x8b, 0x8d, 0xfc, 0x81, 0x91, 0xaf, 0xc6, 0x76, 0xf1, 0x73, 0x40, 0xd4, 0xf8,
-	0x6b, 0xc0, 0xe7, 0x65, 0x1c, 0xf4, 0x8f, 0xc4, 0xe7, 0x39, 0x50, 0xa3, 0xc4, 0xb6, 0xc6, 0x39,
-	0xad, 0x52, 0x62, 0xb7, 0x86, 0xb4, 0x4a, 0xdc, 0x38, 0xb5, 0x11, 0xee, 0xc6, 0x90, 0xdd, 0x73,
-	0xa0, 0xe6, 0x50, 0x96, 0xda, 0x4f, 0x54, 0xdb, 0xaa, 0x43, 0x59, 0x7a, 0xbf, 0x08, 0x97, 0xdc,
-	0xaf, 0x10, 0xe3, 0x12, 0xfb, 0x5d, 0x02, 0xd5, 0xc4, 0xb9, 0x87, 0xd3, 0x5d, 0x25, 0x76, 0x89,
-	0x0f, 0xd8, 0xd5, 0xc4, 0x41, 0x87, 0xab, 0xd1, 0x95, 0xd8, 0x87, 0x1d, 0xc7, 0xf8, 0x23, 0x3d,
-	0x6d, 0x1e, 0x27, 0xf5, 0xaa, 0x47, 0x6f, 0x37, 0x99, 0xc7, 0xcb, 0x03, 0xb4, 0x5c, 0x9c, 0x49,
-	0xcb, 0x3f, 0x2a, 0x72, 0x5c, 0x35, 0x11, 0xcf, 0x9c, 0x63, 0x56, 0x21, 0xb2, 0x70, 0x3c, 0x71,
-	0x5a, 0x93, 0xc1, 0x8c, 0xb8, 0xa5, 0x4c, 0x9a, 0xa2, 0x87, 0xa7, 0xe6, 0xb2, 0x3c, 0xd9, 0x99,
-	0xa6, 0xb5, 0x2f, 0x72, 0xa9, 0xb7, 0x04, 0x29, 0xf7, 0x39, 0xbe, 0x25, 0xcc, 0x51, 0xda, 0xe9,
-	0xf1, 0xa6, 0x30, 0xd3, 0xa0, 0x7d, 0x2f, 0x07, 0x56, 0x12, 0xd3, 0xeb, 0xdb, 0x04, 0x87, 0x41,
-	0xe6, 0x2f, 0x11, 0x2f, 0x03, 0xd0, 0x8d, 0x0c, 0x93, 0x1c, 0x94, 0xf9, 0x0a, 0x8f, 0xf0, 0x35,
-	0x50, 0x12, 0xb7, 0x0f, 0xf7, 0x9e, 0xb4, 0xc8, 0xc1, 0xe3, 0x1f, 0x09, 0xd4, 0xcc, 0x33, 0xcb,
-	0x36, 0x58, 0xf4, 0x90, 0xd7, 0x41, 0x24, 0x4a, 0xf8, 0x83, 0x93, 0x74, 0x00, 0xd4, 0x56, 0x41,
-	0x01, 0xdd, 0x61, 0x04, 0xca, 0xd7, 0x22, 0x71, 0x61, 0x7c, 0xad, 0x48, 0x9a, 0x44, 0x35, 0x1c,
-	0xa1, 0x29, 0x37, 0x0b, 0x4d, 0xf9, 0x83, 0x68, 0x52, 0x0f, 0x4f, 0x93, 0xf1, 0xab, 0x22, 0xbb,
-	0xe2, 0xbb, 0x08, 0xee, 0x4b, 0xd7, 0x2e, 0x81, 0x65, 0x11, 0x50, 0x5c, 0xf1, 0xa6, 0x3d, 0xca,
-	0xaa, 0xc0, 0x0f, 0xca, 0xdd, 0x31, 0x89, 0xed, 0xf7, 0x9c, 0xac, 0x2c, 0x22, 0x5d, 0x79, 0x70,
-	0xd7, 0xb8, 0xa3, 0xff, 0xd1, 0x87, 0x8f, 0xf9, 0xc4, 0xa5, 0xbd, 0x39, 0x78, 0x3e, 0xd4, 0x62,
-	0x38, 0x7a, 0x46, 0x53, 0x05, 0xba, 0x24, 0xf1, 0x37, 0x71, 0xd3, 0x71, 0xb4, 0x2b, 0xe0, 0x44,
-	0xc2, 0x5e, 0x54, 0xb7, 0xa9, 0x8d, 0xa8, 0x16, 0x6f, 0x21, 0x54, 0x6c, 0xfc, 0xad, 0xa4, 0x8a,
-	0x21, 0x67, 0xf7, 0x6a, 0xa4, 0xf7, 0xe7, 0x9b, 0xdc, 0x38, 0x85, 0x0b, 0xc9, 0x14, 0xbe, 0xa7,
-	0xc8, 0x81, 0xe6, 0x9a, 0x4b, 0x08, 0x26, 0x47, 0xfa, 0x7c, 0x96, 0xed, 0xdb, 0x50, 0x96, 0xcf,
-	0x61, 0xc6, 0xe7, 0x8a, 0x7c, 0x93, 0x4a, 0xba, 0x69, 0x22, 0x1a, 0xf6, 0x58, 0xd4, 0xf1, 0xe4,
-	0x24, 0x1e, 0xb9, 0x5a, 0x8d, 0xe7, 0xec, 0x39, 0xba, 0xf3, 0x43, 0x9a, 0xb5, 0xe7, 0x76, 0x44,
-	0xf9, 0x2e, 0x4d, 0xab, 0x88, 0xe3, 0xa8, 0xb4, 0xce, 0xd1, 0xdf, 0xb8, 0xe1, 0x08, 0x7f, 0x8f,
-	0x53, 0x5f, 0x36, 0x3e, 0x1b, 0x8c, 0x7b, 0x09, 0xdf, 0xa6, 0x50, 0x39, 0x27, 0x4f, 0xf6, 0xe5,
-	0x43, 0x6d, 0x33, 0xd8, 0x43, 0xef, 0xe1, 0x9e, 0x6b, 0xf7, 0x2f, 0xf7, 0x10, 0xf4, 0xc3, 0x40,
-	0x3b, 0x0d, 0x4a, 0x9d, 0x1e, 0xb6, 0x6f, 0x5f, 0x0f, 0x3d, 0xee, 0x4b, 0xde, 0x8c, 0xaf, 0xa3,
-	0xc1, 0x42, 0x4e, 0x88, 0xae, 0xbf, 0x8b, 0xb9, 0x3b, 0x95, 0xc9, 0x83, 0x85, 0x28, 0x94, 0xd1,
-	0x7c, 0x68, 0x02, 0x27, 0xfe, 0xdd, 0xda, 0x79, 0xf0, 0xa4, 0xae, 0x3c, 0x7c, 0x52, 0x57, 0xfe,
-	0x7c, 0x52, 0x57, 0xbe, 0x7c, 0x5a, 0x5f, 0x78, 0xf8, 0xb4, 0xbe, 0xf0, 0xcb, 0xd3, 0xfa, 0xc2,
-	0x87, 0x5b, 0x5d, 0x97, 0xed, 0x85, 0x9d, 0x4d, 0x1b, 0x7b, 0x5b, 0x1d, 0xbf, 0x73, 0xde, 0xde,
-	0x83, 0xae, 0xbf, 0x95, 0xf8, 0x3f, 0xe8, 0x4e, 0xfa, 0x1f, 0xa1, 0x4e, 0x91, 0xff, 0x25, 0xf4,
-	0xea, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x09, 0x03, 0xbd, 0x44, 0xbb, 0x1a, 0x00, 0x00,
+	// 1585 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x59, 0xbf, 0x6f, 0xdb, 0xc6,
+	0x17, 0x37, 0x25, 0x4a, 0x96, 0x4f, 0x96, 0x9d, 0x30, 0x8e, 0xc3, 0x38, 0xf9, 0xca, 0x0a, 0x87,
+	0xc0, 0x5f, 0xa0, 0xb1, 0x01, 0x27, 0x6d, 0xd3, 0xa5, 0x81, 0xed, 0xa4, 0x81, 0xd0, 0xe6, 0x47,
+	0xa9, 0x24, 0x43, 0x17, 0xe2, 0x44, 0x9e, 0x69, 0x36, 0x24, 0x8f, 0x3d, 0x9e, 0xd4, 0x28, 0xff,
+	0x43, 0x81, 0xae, 0xdd, 0x3b, 0x14, 0x28, 0x0a, 0x74, 0x08, 0xba, 0x75, 0x4f, 0x3b, 0xa5, 0xe9,
+	0xd2, 0x1f, 0x40, 0x50, 0x24, 0x53, 0xba, 0x74, 0x2d, 0x3a, 0x15, 0xbc, 0x3b, 0x51, 0xa4, 0x28,
+	0x47, 0xa6, 0x52, 0xc7, 0xce, 0x26, 0x3e, 0x7d, 0xde, 0xdd, 0x7b, 0x9f, 0xfb, 0xdc, 0xbb, 0xc7,
+	0x23, 0x58, 0xb6, 0x09, 0x42, 0xfe, 0xb6, 0x83, 0x5c, 0x6b, 0x2d, 0xa4, 0x98, 0x40, 0x1b, 0xad,
+	0xa1, 0x2e, 0xf2, 0x69, 0xb8, 0x1a, 0x10, 0x4c, 0xb1, 0xa2, 0x0c, 0x00, 0xab, 0x02, 0xb0, 0x74,
+	0xd2, 0xc4, 0xa1, 0x87, 0x43, 0x83, 0x21, 0xd6, 0xf8, 0x03, 0x87, 0x2f, 0x2d, 0xd8, 0xd8, 0xc6,
+	0xdc, 0x1e, 0xfd, 0x12, 0xd6, 0x51, 0xb3, 0x98, 0xd8, 0xf3, 0xb0, 0x2f, 0x00, 0xf5, 0x11, 0x00,
+	0xda, 0x0b, 0x90, 0x18, 0x56, 0xfb, 0x59, 0x06, 0x47, 0xaf, 0x44, 0x61, 0x6d, 0x11, 0x04, 0x29,
+	0xda, 0xec, 0x98, 0x77, 0x11, 0x55, 0x56, 0x41, 0x09, 0x7f, 0xea, 0x23, 0xa2, 0x4a, 0x0d, 0x69,
+	0x65, 0x66, 0x53, 0x7d, 0xfc, 0xe0, 0xdc, 0x82, 0x88, 0x66, 0xc3, 0xb2, 0x08, 0x0a, 0xc3, 0x16,
+	0x25, 0x8e, 0x6f, 0xeb, 0x1c, 0xa6, 0x2c, 0x83, 0x6a, 0x9b, 0x79, 0x1a, 0x3e, 0xf4, 0x90, 0x5a,
+	0x88, 0xbc, 0x74, 0xc0, 0x4d, 0xd7, 0xa1, 0x87, 0x94, 0x4d, 0x00, 0xba, 0x4e, 0xe8, 0xb4, 0x1d,
+	0xd7, 0xa1, 0x3d, 0xb5, 0xd8, 0x90, 0x56, 0xe6, 0xd6, 0xb5, 0xd5, 0x2c, 0x03, 0xab, 0x77, 0x62,
+	0xd4, 0xad, 0x5e, 0x80, 0xf4, 0x84, 0x97, 0x72, 0x0a, 0xcc, 0x98, 0x2c, 0x48, 0x03, 0x52, 0x55,
+	0x6e, 0x48, 0x2b, 0x45, 0xbd, 0xc2, 0x0d, 0x1b, 0x54, 0xb9, 0x08, 0x66, 0x44, 0x04, 0x8e, 0xa5,
+	0x96, 0x58, 0xd4, 0xa7, 0x1e, 0x3e, 0x59, 0x9e, 0xfa, 0xed, 0xc9, 0xb2, 0x7c, 0xdb, 0xf1, 0xe9,
+	0xe3, 0x07, 0xe7, 0xaa, 0x22, 0x83, 0xe8, 0x51, 0xaf, 0x70, 0x74, 0xd3, 0x52, 0x2e, 0x81, 0x6a,
+	0x88, 0x3b, 0xc4, 0x44, 0x46, 0xc4, 0x8b, 0x5a, 0x66, 0xb1, 0xd5, 0x47, 0xc5, 0xd6, 0x62, 0x30,
+	0x1e, 0x57, 0x18, 0xff, 0x56, 0xde, 0x00, 0x8a, 0xb9, 0x03, 0x89, 0x8d, 0x2c, 0x83, 0x20, 0x68,
+	0x19, 0x9f, 0x74, 0x30, 0x85, 0xea, 0x74, 0x43, 0x5a, 0x91, 0xf5, 0x23, 0xe2, 0x1f, 0x1d, 0x41,
+	0xeb, 0xc3, 0xc8, 0xae, 0x6c, 0x80, 0xf9, 0x00, 0xf6, 0x3c, 0xe4, 0x53, 0x03, 0x72, 0x2a, 0xd5,
+	0xca, 0x18, 0x92, 0xe7, 0x84, 0x83, 0xb0, 0x2a, 0x1a, 0xa8, 0x05, 0xc4, 0xf1, 0x20, 0xe9, 0x19,
+	0x61, 0x10, 0xe5, 0x3b, 0xd3, 0x90, 0x56, 0x6a, 0x7a, 0x55, 0x18, 0x5b, 0x41, 0xd3, 0x52, 0x36,
+	0x41, 0xdd, 0x76, 0x71, 0x1b, 0xba, 0x46, 0xd7, 0x21, 0xb4, 0x03, 0x5d, 0xc3, 0x26, 0xb8, 0x13,
+	0x18, 0xdb, 0xd0, 0x73, 0xdc, 0x5e, 0xe4, 0x04, 0x98, 0xd3, 0x12, 0x47, 0xdd, 0xe1, 0xa0, 0xab,
+	0x11, 0xe6, 0x3d, 0x06, 0x69, 0x5a, 0xca, 0x45, 0x50, 0x0e, 0x29, 0xa4, 0x9d, 0x50, 0xad, 0x32,
+	0x52, 0x1a, 0xa3, 0x48, 0xe1, 0x8a, 0x69, 0x31, 0x9c, 0x2e, 0xf0, 0xda, 0xdf, 0x92, 0x50, 0xd5,
+	0x65, 0xe4, 0xa2, 0x58, 0x55, 0x17, 0x40, 0x05, 0x07, 0x88, 0x40, 0x8a, 0xc7, 0x0b, 0x2b, 0x46,
+	0x0e, 0xb4, 0x58, 0x98, 0x48, 0x8b, 0xc5, 0x8c, 0x16, 0x53, 0x52, 0x91, 0xf3, 0x48, 0x25, 0x43,
+	0x7c, 0x29, 0x43, 0xbc, 0xf6, 0x63, 0x11, 0x1c, 0x67, 0xa9, 0xdf, 0x0e, 0xac, 0x78, 0x43, 0x35,
+	0xfd, 0x6d, 0x3c, 0x61, 0xfa, 0x63, 0xb7, 0x56, 0x2a, 0x9d, 0x62, 0x9e, 0x74, 0xde, 0x01, 0x27,
+	0xb3, 0xc2, 0x35, 0xda, 0x68, 0x1b, 0x13, 0xc4, 0x88, 0x91, 0xf5, 0xc5, 0x61, 0xfd, 0x6e, 0xb2,
+	0x7f, 0x95, 0xb7, 0x81, 0x3a, 0xc2, 0x15, 0x6e, 0x53, 0x44, 0x18, 0x29, 0xb2, 0x7e, 0x7c, 0xd8,
+	0x73, 0x23, 0xfa, 0x53, 0xb9, 0x00, 0x16, 0x87, 0xe4, 0xdf, 0x9f, 0xb0, 0xcc, 0x32, 0x5b, 0x48,
+	0x6b, 0x5d, 0x4c, 0xb7, 0x0e, 0x8e, 0x0f, 0x7b, 0xf1, 0xb9, 0xa6, 0x99, 0xd3, 0xb1, 0xb4, 0x13,
+	0x9f, 0x29, 0x5d, 0x72, 0x2a, 0x93, 0x94, 0x1c, 0xed, 0x2b, 0x09, 0x2c, 0x72, 0x1d, 0x3b, 0xa1,
+	0x89, 0x7d, 0xea, 0xf8, 0x9d, 0xbe, 0x98, 0x53, 0xb4, 0x4b, 0x79, 0x68, 0x1f, 0xbb, 0xa2, 0x8b,
+	0xa0, 0x4c, 0x10, 0x0c, 0xb1, 0x2f, 0xc4, 0x2b, 0x9e, 0xa2, 0x02, 0x68, 0xb1, 0xfd, 0x94, 0x28,
+	0x80, 0xdc, 0xb0, 0x41, 0xb5, 0x27, 0xa5, 0x54, 0x21, 0xbf, 0xd1, 0xfe, 0x18, 0x99, 0x54, 0x59,
+	0x07, 0xd3, 0xac, 0x44, 0xee, 0x41, 0x72, 0x7d, 0xe0, 0x7f, 0xbf, 0xe1, 0x96, 0x41, 0x15, 0xb3,
+	0x70, 0x38, 0x40, 0xe6, 0x00, 0x6e, 0xca, 0x4a, 0xb8, 0x9c, 0x87, 0xcb, 0x8b, 0x60, 0x46, 0x0c,
+	0xed, 0x58, 0x5c, 0x0c, 0x63, 0x3c, 0x39, 0x7a, 0xd4, 0x5e, 0xae, 0x64, 0x8b, 0xe8, 0x19, 0x30,
+	0x1b, 0xc0, 0x9e, 0x8b, 0xa1, 0x65, 0x84, 0xce, 0x7d, 0xc4, 0xea, 0xac, 0xac, 0x57, 0x85, 0xad,
+	0xe5, 0xdc, 0x1f, 0x3e, 0xd8, 0xc0, 0x44, 0x07, 0xdb, 0x19, 0x30, 0x1b, 0x89, 0x2b, 0x52, 0x37,
+	0x3b, 0x82, 0xaa, 0x8c, 0xa0, 0xaa, 0xb0, 0xb1, 0x33, 0x26, 0x75, 0xf6, 0xcd, 0x66, 0xce, 0xbe,
+	0x7e, 0x9d, 0xae, 0xed, 0x5e, 0xa7, 0xb9, 0x20, 0xd2, 0x75, 0x5a, 0x79, 0x1f, 0xcc, 0x13, 0x64,
+	0x75, 0x7c, 0x0b, 0xfa, 0x66, 0x8f, 0x4f, 0x3e, 0xb7, 0x7b, 0x0a, 0x7a, 0x0c, 0x65, 0x29, 0xcc,
+	0x91, 0xd4, 0xf3, 0xf0, 0x41, 0x3a, 0x9f, 0xfb, 0x20, 0x3d, 0x0d, 0x66, 0xcc, 0x1d, 0x64, 0xde,
+	0x0d, 0x3b, 0x5e, 0xa8, 0x1e, 0x69, 0x14, 0x57, 0x66, 0xf5, 0x81, 0x41, 0xfb, 0x4b, 0x02, 0x27,
+	0xb8, 0xc0, 0xa1, 0x6f, 0x22, 0x37, 0x25, 0xf3, 0x7d, 0x2a, 0xad, 0x43, 0xc2, 0x2d, 0x66, 0x84,
+	0x9b, 0x11, 0x91, 0x9c, 0x15, 0x51, 0x4a, 0xa2, 0xe5, 0x1c, 0x12, 0xd5, 0x9e, 0x17, 0xc0, 0x3c,
+	0xcb, 0xb8, 0x85, 0xa0, 0x7b, 0xc0, 0x99, 0xa6, 0xb2, 0x28, 0xe5, 0xd9, 0x68, 0x03, 0x75, 0x96,
+	0x73, 0xaa, 0xf3, 0x4d, 0x70, 0x62, 0x64, 0x0f, 0x23, 0xb6, 0x7a, 0x4d, 0x5f, 0xc8, 0x36, 0x2f,
+	0x4d, 0x4b, 0x39, 0x0f, 0x16, 0x5d, 0x6c, 0x8e, 0xf2, 0xe2, 0x5b, 0xfc, 0x18, 0xfb, 0x37, 0xed,
+	0x34, 0xe0, 0x7a, 0x0b, 0x07, 0xbd, 0x97, 0xe2, 0xfa, 0x2c, 0x98, 0x0f, 0x89, 0x69, 0x64, 0xf9,
+	0xae, 0x85, 0xc4, 0xdc, 0x1c, 0x50, 0x2e, 0x70, 0x59, 0xda, 0x23, 0xdc, 0x8d, 0x01, 0xf3, 0x67,
+	0xc1, 0xbc, 0x15, 0xd2, 0xd4, 0x78, 0xbc, 0x82, 0xd6, 0xac, 0x90, 0xa6, 0xc7, 0x8b, 0x70, 0xc9,
+	0xf1, 0x4a, 0x31, 0x2e, 0x31, 0xde, 0x25, 0x50, 0x4b, 0xcc, 0xbb, 0x37, 0x4d, 0x56, 0xe3, 0x90,
+	0x58, 0xc3, 0x5c, 0x4b, 0x4c, 0xb4, 0xb7, 0xba, 0x5b, 0x8d, 0x63, 0x68, 0x5a, 0xda, 0x3f, 0xe9,
+	0xee, 0xf0, 0x30, 0x29, 0x5b, 0xce, 0xa3, 0xec, 0xdd, 0x85, 0x56, 0xda, 0x5d, 0x68, 0x3f, 0x48,
+	0xa2, 0x3f, 0xd4, 0x11, 0x93, 0xfc, 0x21, 0xdb, 0xda, 0x79, 0x08, 0x18, 0xd9, 0x1e, 0x89, 0x64,
+	0x86, 0xc2, 0x92, 0x46, 0xb5, 0xad, 0x83, 0x59, 0x0b, 0x79, 0x68, 0x9f, 0xa8, 0x3d, 0xfa, 0xac,
+	0x90, 0x6a, 0xcb, 0x85, 0x16, 0xf7, 0xb1, 0x2d, 0xdf, 0x47, 0xdd, 0xa5, 0x7b, 0x8e, 0xd2, 0x44,
+	0x9d, 0xed, 0x97, 0x05, 0x70, 0x24, 0xd1, 0x2e, 0x32, 0x75, 0xe6, 0x7e, 0xed, 0xff, 0x1f, 0x00,
+	0x5c, 0xf2, 0x09, 0x0e, 0x66, 0x98, 0x85, 0x65, 0xf8, 0x16, 0xa8, 0xc4, 0x3b, 0x62, 0x0f, 0x2f,
+	0x26, 0xd3, 0xb6, 0x28, 0xe0, 0x43, 0x8d, 0x84, 0x9c, 0xbb, 0x91, 0x58, 0x07, 0xd3, 0x1e, 0xf2,
+	0xda, 0x88, 0x84, 0x6a, 0xa9, 0x51, 0x7c, 0x71, 0xd7, 0x2b, 0x80, 0xca, 0x02, 0x28, 0xa1, 0x7b,
+	0x94, 0x40, 0xf1, 0x1e, 0xc2, 0x1f, 0xb4, 0x2f, 0x24, 0x41, 0x13, 0x2f, 0x55, 0x43, 0x34, 0x15,
+	0x26, 0xa1, 0xa9, 0xf8, 0x22, 0x9a, 0xe4, 0xbd, 0xd3, 0xa4, 0xfd, 0x2a, 0x89, 0x23, 0xeb, 0x03,
+	0x04, 0xbb, 0x22, 0xb4, 0x4b, 0x60, 0x8e, 0x27, 0x14, 0x5f, 0x2e, 0x8c, 0x5b, 0xca, 0x1a, 0xc7,
+	0xf7, 0xef, 0x16, 0x0e, 0x49, 0x6e, 0xbf, 0x17, 0x44, 0x65, 0xe1, 0xdb, 0x95, 0x25, 0x77, 0x8d,
+	0x05, 0xfa, 0x8a, 0x6e, 0x11, 0xf6, 0x27, 0x2f, 0xe5, 0xdd, 0xfe, 0xfa, 0x84, 0x06, 0xc5, 0xd1,
+	0x1a, 0x8d, 0x15, 0xe8, 0xac, 0xc0, 0xdf, 0xc2, 0x1b, 0x96, 0xa5, 0x5c, 0x06, 0x47, 0x13, 0xfe,
+	0xbc, 0xba, 0xa9, 0xe5, 0x31, 0x43, 0xcc, 0xc7, 0x43, 0x70, 0x15, 0x6b, 0x7f, 0x4a, 0xa9, 0x62,
+	0xc8, 0xd8, 0xbd, 0x12, 0xe9, 0xfd, 0xf5, 0x26, 0x37, 0xde, 0xc2, 0xa5, 0xe4, 0x16, 0x7e, 0xd8,
+	0xef, 0x36, 0xae, 0x39, 0x84, 0x60, 0xf2, 0x52, 0x77, 0x51, 0xf9, 0x2e, 0x63, 0xf2, 0xde, 0x2d,
+	0x59, 0x28, 0xa4, 0x86, 0xb9, 0x03, 0x1d, 0x3f, 0x71, 0xb7, 0x14, 0x19, 0xb7, 0x22, 0x5b, 0xd3,
+	0xd2, 0xbe, 0xed, 0xbf, 0x02, 0x25, 0x53, 0xd1, 0x51, 0xd8, 0x71, 0x69, 0x74, 0x2a, 0x8a, 0x36,
+	0x5b, 0x62, 0x8e, 0xfd, 0x26, 0xfa, 0x80, 0x43, 0x7e, 0x9e, 0x66, 0xff, 0xb5, 0xed, 0xf5, 0xf6,
+	0x92, 0xeb, 0x4f, 0xe9, 0xe5, 0xe1, 0xb9, 0xbe, 0xec, 0xf2, 0x1c, 0x70, 0x4e, 0xdf, 0xf7, 0x0f,
+	0x40, 0x9e, 0xd3, 0xa1, 0xea, 0x13, 0x32, 0xf1, 0xcb, 0xd9, 0xf8, 0xbf, 0xee, 0xb7, 0xa8, 0x89,
+	0xf8, 0xc7, 0x2c, 0xc9, 0x01, 0x46, 0xdb, 0x15, 0x02, 0x6a, 0x51, 0xe8, 0xa2, 0x9b, 0xd8, 0x75,
+	0xcc, 0xde, 0x96, 0x8b, 0xa0, 0xdf, 0x09, 0x94, 0x25, 0x50, 0x69, 0xbb, 0xd8, 0xbc, 0x7b, 0xbd,
+	0xe3, 0xb1, 0x78, 0x8b, 0x7a, 0xfc, 0x1c, 0x35, 0x4c, 0xa2, 0xf3, 0x75, 0xfc, 0x6d, 0xcc, 0x42,
+	0xae, 0x8e, 0x6e, 0x98, 0xf8, 0x01, 0x10, 0xf5, 0xbd, 0x3a, 0xb0, 0xe2, 0xdf, 0xda, 0x63, 0x09,
+	0x2c, 0x08, 0x96, 0x6c, 0x02, 0xa9, 0x83, 0xfd, 0x57, 0x58, 0x26, 0x73, 0xdd, 0x59, 0xff, 0x1f,
+	0x1c, 0x8d, 0x5e, 0x3e, 0x47, 0xdd, 0xba, 0xcc, 0x59, 0x21, 0xbd, 0x99, 0xb8, 0x89, 0xff, 0x46,
+	0x02, 0x4b, 0x89, 0x0b, 0xa3, 0xc3, 0x9e, 0x9a, 0xf6, 0x5d, 0x01, 0x9c, 0x16, 0x57, 0x10, 0x5e,
+	0x10, 0x2d, 0xcd, 0xa1, 0x5f, 0x8c, 0xf1, 0x1f, 0x99, 0xe4, 0xb1, 0x1f, 0x99, 0x36, 0xc0, 0xac,
+	0xdd, 0xb5, 0x0d, 0x0f, 0x06, 0x81, 0xe3, 0xdb, 0xbc, 0x61, 0xdf, 0x45, 0xbc, 0x57, 0xef, 0x5c,
+	0xbd, 0xc6, 0x61, 0x7a, 0xd5, 0xee, 0xda, 0xe2, 0x77, 0xb8, 0xd9, 0x7c, 0xf8, 0xb4, 0x2e, 0x3d,
+	0x7a, 0x5a, 0x97, 0xfe, 0x78, 0x5a, 0x97, 0x3e, 0x7f, 0x56, 0x9f, 0x7a, 0xf4, 0xac, 0x3e, 0xf5,
+	0xcb, 0xb3, 0xfa, 0xd4, 0x47, 0x6b, 0xb6, 0x43, 0x77, 0x3a, 0xed, 0x55, 0x13, 0x7b, 0x6b, 0x6d,
+	0xbf, 0x7d, 0x8e, 0xed, 0xbd, 0xb5, 0xc4, 0x27, 0xd1, 0x7b, 0xe9, 0x8f, 0xa2, 0xed, 0x32, 0xfb,
+	0x2a, 0x7a, 0xfe, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf3, 0x5a, 0x77, 0xe1, 0xbe, 0x1d, 0x00,
+	0x00,
 }
 
 func (m *EventCreateBucket) Marshal() (dAtA []byte, err error) {
@@ -2005,14 +2280,17 @@ func (m *EventCreateBucket) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	if m.Status != 0 {
 		i = encodeVarintEvents(dAtA, i, uint64(m.Status))
 		i--
+		dAtA[i] = 0x58
+	}
+	if m.GlobalVirtualGroupFamilyId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.GlobalVirtualGroupFamilyId))
+		i--
 		dAtA[i] = 0x50
 	}
-	if len(m.PrimarySpAddress) > 0 {
-		i -= len(m.PrimarySpAddress)
-		copy(dAtA[i:], m.PrimarySpAddress)
-		i = encodeVarintEvents(dAtA, i, uint64(len(m.PrimarySpAddress)))
+	if m.PrimarySpId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.PrimarySpId))
 		i--
-		dAtA[i] = 0x4a
+		dAtA[i] = 0x48
 	}
 	if len(m.PaymentAddress) > 0 {
 		i -= len(m.PaymentAddress)
@@ -2088,12 +2366,10 @@ func (m *EventDeleteBucket) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	_ = i
 	var l int
 	_ = l
-	if len(m.PrimarySpAddress) > 0 {
-		i -= len(m.PrimarySpAddress)
-		copy(dAtA[i:], m.PrimarySpAddress)
-		i = encodeVarintEvents(dAtA, i, uint64(len(m.PrimarySpAddress)))
+	if m.PrimarySpId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.PrimarySpId))
 		i--
-		dAtA[i] = 0x2a
+		dAtA[i] = 0x28
 	}
 	{
 		size := m.BucketId.Size()
@@ -2325,12 +2601,10 @@ func (m *EventCreateObject) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		i--
 		dAtA[i] = 0x48
 	}
-	if len(m.PrimarySpAddress) > 0 {
-		i -= len(m.PrimarySpAddress)
-		copy(dAtA[i:], m.PrimarySpAddress)
-		i = encodeVarintEvents(dAtA, i, uint64(len(m.PrimarySpAddress)))
+	if m.PrimarySpId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.PrimarySpId))
 		i--
-		dAtA[i] = 0x42
+		dAtA[i] = 0x40
 	}
 	{
 		size := m.ObjectId.Size()
@@ -2413,12 +2687,10 @@ func (m *EventCancelCreateObject) MarshalToSizedBuffer(dAtA []byte) (int, error)
 	}
 	i--
 	dAtA[i] = 0x32
-	if len(m.PrimarySpAddress) > 0 {
-		i -= len(m.PrimarySpAddress)
-		copy(dAtA[i:], m.PrimarySpAddress)
-		i = encodeVarintEvents(dAtA, i, uint64(len(m.PrimarySpAddress)))
+	if m.PrimarySpId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.PrimarySpId))
 		i--
-		dAtA[i] = 0x22
+		dAtA[i] = 0x20
 	}
 	if len(m.ObjectName) > 0 {
 		i -= len(m.ObjectName)
@@ -2464,14 +2736,15 @@ func (m *EventSealObject) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	_ = i
 	var l int
 	_ = l
-	if len(m.SecondarySpAddresses) > 0 {
-		for iNdEx := len(m.SecondarySpAddresses) - 1; iNdEx >= 0; iNdEx-- {
-			i -= len(m.SecondarySpAddresses[iNdEx])
-			copy(dAtA[i:], m.SecondarySpAddresses[iNdEx])
-			i = encodeVarintEvents(dAtA, i, uint64(len(m.SecondarySpAddresses[iNdEx])))
-			i--
-			dAtA[i] = 0x3a
-		}
+	if m.LocalVirtualGroupId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.LocalVirtualGroupId))
+		i--
+		dAtA[i] = 0x40
+	}
+	if m.GlobalVirtualGroupId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.GlobalVirtualGroupId))
+		i--
+		dAtA[i] = 0x38
 	}
 	if m.Status != 0 {
 		i = encodeVarintEvents(dAtA, i, uint64(m.Status))
@@ -2610,21 +2883,10 @@ func (m *EventDeleteObject) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	_ = i
 	var l int
 	_ = l
-	if len(m.SecondarySpAddresses) > 0 {
-		for iNdEx := len(m.SecondarySpAddresses) - 1; iNdEx >= 0; iNdEx-- {
-			i -= len(m.SecondarySpAddresses[iNdEx])
-			copy(dAtA[i:], m.SecondarySpAddresses[iNdEx])
-			i = encodeVarintEvents(dAtA, i, uint64(len(m.SecondarySpAddresses[iNdEx])))
-			i--
-			dAtA[i] = 0x32
-		}
-	}
-	if len(m.PrimarySpAddress) > 0 {
-		i -= len(m.PrimarySpAddress)
-		copy(dAtA[i:], m.PrimarySpAddress)
-		i = encodeVarintEvents(dAtA, i, uint64(len(m.PrimarySpAddress)))
+	if m.LocalVirtualGroupId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.LocalVirtualGroupId))
 		i--
-		dAtA[i] = 0x2a
+		dAtA[i] = 0x28
 	}
 	{
 		size := m.ObjectId.Size()
@@ -3147,6 +3409,11 @@ func (m *EventMirrorBucket) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	_ = i
 	var l int
 	_ = l
+	if m.DestChainId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.DestChainId))
+		i--
+		dAtA[i] = 0x28
+	}
 	{
 		size := m.BucketId.Size()
 		i -= size
@@ -3194,6 +3461,11 @@ func (m *EventMirrorBucketResult) MarshalToSizedBuffer(dAtA []byte) (int, error)
 	_ = i
 	var l int
 	_ = l
+	if m.DestChainId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.DestChainId))
+		i--
+		dAtA[i] = 0x28
+	}
 	{
 		size := m.BucketId.Size()
 		i -= size
@@ -3239,6 +3511,11 @@ func (m *EventMirrorObject) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	_ = i
 	var l int
 	_ = l
+	if m.DestChainId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.DestChainId))
+		i--
+		dAtA[i] = 0x28
+	}
 	{
 		size := m.ObjectId.Size()
 		i -= size
@@ -3293,6 +3570,11 @@ func (m *EventMirrorObjectResult) MarshalToSizedBuffer(dAtA []byte) (int, error)
 	_ = i
 	var l int
 	_ = l
+	if m.DestChainId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.DestChainId))
+		i--
+		dAtA[i] = 0x28
+	}
 	{
 		size := m.ObjectId.Size()
 		i -= size
@@ -3345,6 +3627,11 @@ func (m *EventMirrorGroup) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	_ = i
 	var l int
 	_ = l
+	if m.DestChainId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.DestChainId))
+		i--
+		dAtA[i] = 0x20
+	}
 	{
 		size := m.GroupId.Size()
 		i -= size
@@ -3392,6 +3679,11 @@ func (m *EventMirrorGroupResult) MarshalToSizedBuffer(dAtA []byte) (int, error)
 	_ = i
 	var l int
 	_ = l
+	if m.DestChainId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.DestChainId))
+		i--
+		dAtA[i] = 0x20
+	}
 	{
 		size := m.GroupId.Size()
 		i -= size
@@ -3457,28 +3749,193 @@ func (m *EventStalePolicyCleanup) MarshalToSizedBuffer(dAtA []byte) (int, error)
 	return len(dAtA) - i, nil
 }
 
-func encodeVarintEvents(dAtA []byte, offset int, v uint64) int {
-	offset -= sovEvents(v)
-	base := offset
-	for v >= 1<<7 {
-		dAtA[offset] = uint8(v&0x7f | 0x80)
-		v >>= 7
-		offset++
+func (m *EventMigrationBucket) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
 	}
-	dAtA[offset] = uint8(v)
-	return base
+	return dAtA[:n], nil
 }
-func (m *EventCreateBucket) Size() (n int) {
-	if m == nil {
-		return 0
-	}
+
+func (m *EventMigrationBucket) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *EventMigrationBucket) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
 	var l int
 	_ = l
-	l = len(m.Owner)
-	if l > 0 {
-		n += 1 + l + sovEvents(uint64(l))
+	if m.DstPrimarySpId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.DstPrimarySpId))
+		i--
+		dAtA[i] = 0x20
 	}
-	l = len(m.BucketName)
+	{
+		size := m.BucketId.Size()
+		i -= size
+		if _, err := m.BucketId.MarshalTo(dAtA[i:]); err != nil {
+			return 0, err
+		}
+		i = encodeVarintEvents(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0x1a
+	if len(m.BucketName) > 0 {
+		i -= len(m.BucketName)
+		copy(dAtA[i:], m.BucketName)
+		i = encodeVarintEvents(dAtA, i, uint64(len(m.BucketName)))
+		i--
+		dAtA[i] = 0x12
+	}
+	if len(m.Operator) > 0 {
+		i -= len(m.Operator)
+		copy(dAtA[i:], m.Operator)
+		i = encodeVarintEvents(dAtA, i, uint64(len(m.Operator)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *EventCancelMigrationBucket) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *EventCancelMigrationBucket) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *EventCancelMigrationBucket) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	{
+		size := m.BucketId.Size()
+		i -= size
+		if _, err := m.BucketId.MarshalTo(dAtA[i:]); err != nil {
+			return 0, err
+		}
+		i = encodeVarintEvents(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0x1a
+	if len(m.BucketName) > 0 {
+		i -= len(m.BucketName)
+		copy(dAtA[i:], m.BucketName)
+		i = encodeVarintEvents(dAtA, i, uint64(len(m.BucketName)))
+		i--
+		dAtA[i] = 0x12
+	}
+	if len(m.Operator) > 0 {
+		i -= len(m.Operator)
+		copy(dAtA[i:], m.Operator)
+		i = encodeVarintEvents(dAtA, i, uint64(len(m.Operator)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *EventCompleteMigrationBucket) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *EventCompleteMigrationBucket) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *EventCompleteMigrationBucket) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.GvgMappings) > 0 {
+		for iNdEx := len(m.GvgMappings) - 1; iNdEx >= 0; iNdEx-- {
+			{
+				size, err := m.GvgMappings[iNdEx].MarshalToSizedBuffer(dAtA[:i])
+				if err != nil {
+					return 0, err
+				}
+				i -= size
+				i = encodeVarintEvents(dAtA, i, uint64(size))
+			}
+			i--
+			dAtA[i] = 0x2a
+		}
+	}
+	if m.GlobalVirtualGroupFamilyId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.GlobalVirtualGroupFamilyId))
+		i--
+		dAtA[i] = 0x20
+	}
+	{
+		size := m.BucketId.Size()
+		i -= size
+		if _, err := m.BucketId.MarshalTo(dAtA[i:]); err != nil {
+			return 0, err
+		}
+		i = encodeVarintEvents(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0x1a
+	if len(m.BucketName) > 0 {
+		i -= len(m.BucketName)
+		copy(dAtA[i:], m.BucketName)
+		i = encodeVarintEvents(dAtA, i, uint64(len(m.BucketName)))
+		i--
+		dAtA[i] = 0x12
+	}
+	if len(m.Operator) > 0 {
+		i -= len(m.Operator)
+		copy(dAtA[i:], m.Operator)
+		i = encodeVarintEvents(dAtA, i, uint64(len(m.Operator)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func encodeVarintEvents(dAtA []byte, offset int, v uint64) int {
+	offset -= sovEvents(v)
+	base := offset
+	for v >= 1<<7 {
+		dAtA[offset] = uint8(v&0x7f | 0x80)
+		v >>= 7
+		offset++
+	}
+	dAtA[offset] = uint8(v)
+	return base
+}
+func (m *EventCreateBucket) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Owner)
+	if l > 0 {
+		n += 1 + l + sovEvents(uint64(l))
+	}
+	l = len(m.BucketName)
 	if l > 0 {
 		n += 1 + l + sovEvents(uint64(l))
 	}
@@ -3500,9 +3957,11 @@ func (m *EventCreateBucket) Size() (n int) {
 	if l > 0 {
 		n += 1 + l + sovEvents(uint64(l))
 	}
-	l = len(m.PrimarySpAddress)
-	if l > 0 {
-		n += 1 + l + sovEvents(uint64(l))
+	if m.PrimarySpId != 0 {
+		n += 1 + sovEvents(uint64(m.PrimarySpId))
+	}
+	if m.GlobalVirtualGroupFamilyId != 0 {
+		n += 1 + sovEvents(uint64(m.GlobalVirtualGroupFamilyId))
 	}
 	if m.Status != 0 {
 		n += 1 + sovEvents(uint64(m.Status))
@@ -3530,9 +3989,8 @@ func (m *EventDeleteBucket) Size() (n int) {
 	}
 	l = m.BucketId.Size()
 	n += 1 + l + sovEvents(uint64(l))
-	l = len(m.PrimarySpAddress)
-	if l > 0 {
-		n += 1 + l + sovEvents(uint64(l))
+	if m.PrimarySpId != 0 {
+		n += 1 + sovEvents(uint64(m.PrimarySpId))
 	}
 	return n
 }
@@ -3621,9 +4079,8 @@ func (m *EventCreateObject) Size() (n int) {
 	n += 1 + l + sovEvents(uint64(l))
 	l = m.ObjectId.Size()
 	n += 1 + l + sovEvents(uint64(l))
-	l = len(m.PrimarySpAddress)
-	if l > 0 {
-		n += 1 + l + sovEvents(uint64(l))
+	if m.PrimarySpId != 0 {
+		n += 1 + sovEvents(uint64(m.PrimarySpId))
 	}
 	if m.PayloadSize != 0 {
 		n += 1 + sovEvents(uint64(m.PayloadSize))
@@ -3674,9 +4131,8 @@ func (m *EventCancelCreateObject) Size() (n int) {
 	if l > 0 {
 		n += 1 + l + sovEvents(uint64(l))
 	}
-	l = len(m.PrimarySpAddress)
-	if l > 0 {
-		n += 1 + l + sovEvents(uint64(l))
+	if m.PrimarySpId != 0 {
+		n += 1 + sovEvents(uint64(m.PrimarySpId))
 	}
 	l = m.ObjectId.Size()
 	n += 1 + l + sovEvents(uint64(l))
@@ -3706,11 +4162,11 @@ func (m *EventSealObject) Size() (n int) {
 	if m.Status != 0 {
 		n += 1 + sovEvents(uint64(m.Status))
 	}
-	if len(m.SecondarySpAddresses) > 0 {
-		for _, s := range m.SecondarySpAddresses {
-			l = len(s)
-			n += 1 + l + sovEvents(uint64(l))
-		}
+	if m.GlobalVirtualGroupId != 0 {
+		n += 1 + sovEvents(uint64(m.GlobalVirtualGroupId))
+	}
+	if m.LocalVirtualGroupId != 0 {
+		n += 1 + sovEvents(uint64(m.LocalVirtualGroupId))
 	}
 	return n
 }
@@ -3768,15 +4224,8 @@ func (m *EventDeleteObject) Size() (n int) {
 	}
 	l = m.ObjectId.Size()
 	n += 1 + l + sovEvents(uint64(l))
-	l = len(m.PrimarySpAddress)
-	if l > 0 {
-		n += 1 + l + sovEvents(uint64(l))
-	}
-	if len(m.SecondarySpAddresses) > 0 {
-		for _, s := range m.SecondarySpAddresses {
-			l = len(s)
-			n += 1 + l + sovEvents(uint64(l))
-		}
+	if m.LocalVirtualGroupId != 0 {
+		n += 1 + sovEvents(uint64(m.LocalVirtualGroupId))
 	}
 	return n
 }
@@ -4004,6 +4453,9 @@ func (m *EventMirrorBucket) Size() (n int) {
 	}
 	l = m.BucketId.Size()
 	n += 1 + l + sovEvents(uint64(l))
+	if m.DestChainId != 0 {
+		n += 1 + sovEvents(uint64(m.DestChainId))
+	}
 	return n
 }
 
@@ -4022,6 +4474,9 @@ func (m *EventMirrorBucketResult) Size() (n int) {
 	}
 	l = m.BucketId.Size()
 	n += 1 + l + sovEvents(uint64(l))
+	if m.DestChainId != 0 {
+		n += 1 + sovEvents(uint64(m.DestChainId))
+	}
 	return n
 }
 
@@ -4045,6 +4500,9 @@ func (m *EventMirrorObject) Size() (n int) {
 	}
 	l = m.ObjectId.Size()
 	n += 1 + l + sovEvents(uint64(l))
+	if m.DestChainId != 0 {
+		n += 1 + sovEvents(uint64(m.DestChainId))
+	}
 	return n
 }
 
@@ -4067,6 +4525,9 @@ func (m *EventMirrorObjectResult) Size() (n int) {
 	}
 	l = m.ObjectId.Size()
 	n += 1 + l + sovEvents(uint64(l))
+	if m.DestChainId != 0 {
+		n += 1 + sovEvents(uint64(m.DestChainId))
+	}
 	return n
 }
 
@@ -4086,6 +4547,9 @@ func (m *EventMirrorGroup) Size() (n int) {
 	}
 	l = m.GroupId.Size()
 	n += 1 + l + sovEvents(uint64(l))
+	if m.DestChainId != 0 {
+		n += 1 + sovEvents(uint64(m.DestChainId))
+	}
 	return n
 }
 
@@ -4104,6 +4568,9 @@ func (m *EventMirrorGroupResult) Size() (n int) {
 	}
 	l = m.GroupId.Size()
 	n += 1 + l + sovEvents(uint64(l))
+	if m.DestChainId != 0 {
+		n += 1 + sovEvents(uint64(m.DestChainId))
+	}
 	return n
 }
 
@@ -4123,6 +4590,75 @@ func (m *EventStalePolicyCleanup) Size() (n int) {
 	return n
 }
 
+func (m *EventMigrationBucket) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Operator)
+	if l > 0 {
+		n += 1 + l + sovEvents(uint64(l))
+	}
+	l = len(m.BucketName)
+	if l > 0 {
+		n += 1 + l + sovEvents(uint64(l))
+	}
+	l = m.BucketId.Size()
+	n += 1 + l + sovEvents(uint64(l))
+	if m.DstPrimarySpId != 0 {
+		n += 1 + sovEvents(uint64(m.DstPrimarySpId))
+	}
+	return n
+}
+
+func (m *EventCancelMigrationBucket) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Operator)
+	if l > 0 {
+		n += 1 + l + sovEvents(uint64(l))
+	}
+	l = len(m.BucketName)
+	if l > 0 {
+		n += 1 + l + sovEvents(uint64(l))
+	}
+	l = m.BucketId.Size()
+	n += 1 + l + sovEvents(uint64(l))
+	return n
+}
+
+func (m *EventCompleteMigrationBucket) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Operator)
+	if l > 0 {
+		n += 1 + l + sovEvents(uint64(l))
+	}
+	l = len(m.BucketName)
+	if l > 0 {
+		n += 1 + l + sovEvents(uint64(l))
+	}
+	l = m.BucketId.Size()
+	n += 1 + l + sovEvents(uint64(l))
+	if m.GlobalVirtualGroupFamilyId != 0 {
+		n += 1 + sovEvents(uint64(m.GlobalVirtualGroupFamilyId))
+	}
+	if len(m.GvgMappings) > 0 {
+		for _, e := range m.GvgMappings {
+			l = e.Size()
+			n += 1 + l + sovEvents(uint64(l))
+		}
+	}
+	return n
+}
+
 func sovEvents(x uint64) (n int) {
 	return (math_bits.Len64(x|1) + 6) / 7
 }
@@ -4365,10 +4901,10 @@ func (m *EventCreateBucket) Unmarshal(dAtA []byte) error {
 			m.PaymentAddress = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
 		case 9:
-			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field PrimarySpAddress", wireType)
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field PrimarySpId", wireType)
 			}
-			var stringLen uint64
+			m.PrimarySpId = 0
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowEvents
@@ -4378,29 +4914,16 @@ func (m *EventCreateBucket) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				stringLen |= uint64(b&0x7F) << shift
+				m.PrimarySpId |= uint32(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
-			intStringLen := int(stringLen)
-			if intStringLen < 0 {
-				return ErrInvalidLengthEvents
-			}
-			postIndex := iNdEx + intStringLen
-			if postIndex < 0 {
-				return ErrInvalidLengthEvents
-			}
-			if postIndex > l {
-				return io.ErrUnexpectedEOF
-			}
-			m.PrimarySpAddress = string(dAtA[iNdEx:postIndex])
-			iNdEx = postIndex
 		case 10:
 			if wireType != 0 {
-				return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupFamilyId", wireType)
 			}
-			m.Status = 0
+			m.GlobalVirtualGroupFamilyId = 0
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowEvents
@@ -4410,18 +4933,37 @@ func (m *EventCreateBucket) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				m.Status |= BucketStatus(b&0x7F) << shift
+				m.GlobalVirtualGroupFamilyId |= uint32(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
-		default:
-			iNdEx = preIndex
-			skippy, err := skipEvents(dAtA[iNdEx:])
-			if err != nil {
-				return err
-			}
-			if (skippy < 0) || (iNdEx+skippy) < 0 {
+		case 11:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType)
+			}
+			m.Status = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.Status |= BucketStatus(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipEvents(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
 				return ErrInvalidLengthEvents
 			}
 			if (iNdEx + skippy) > l {
@@ -4596,10 +5138,10 @@ func (m *EventDeleteBucket) Unmarshal(dAtA []byte) error {
 			}
 			iNdEx = postIndex
 		case 5:
-			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field PrimarySpAddress", wireType)
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field PrimarySpId", wireType)
 			}
-			var stringLen uint64
+			m.PrimarySpId = 0
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowEvents
@@ -4609,24 +5151,11 @@ func (m *EventDeleteBucket) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				stringLen |= uint64(b&0x7F) << shift
+				m.PrimarySpId |= uint32(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
-			intStringLen := int(stringLen)
-			if intStringLen < 0 {
-				return ErrInvalidLengthEvents
-			}
-			postIndex := iNdEx + intStringLen
-			if postIndex < 0 {
-				return ErrInvalidLengthEvents
-			}
-			if postIndex > l {
-				return io.ErrUnexpectedEOF
-			}
-			m.PrimarySpAddress = string(dAtA[iNdEx:postIndex])
-			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipEvents(dAtA[iNdEx:])
@@ -5310,10 +5839,10 @@ func (m *EventCreateObject) Unmarshal(dAtA []byte) error {
 			}
 			iNdEx = postIndex
 		case 8:
-			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field PrimarySpAddress", wireType)
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field PrimarySpId", wireType)
 			}
-			var stringLen uint64
+			m.PrimarySpId = 0
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowEvents
@@ -5323,24 +5852,11 @@ func (m *EventCreateObject) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				stringLen |= uint64(b&0x7F) << shift
+				m.PrimarySpId |= uint32(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
-			intStringLen := int(stringLen)
-			if intStringLen < 0 {
-				return ErrInvalidLengthEvents
-			}
-			postIndex := iNdEx + intStringLen
-			if postIndex < 0 {
-				return ErrInvalidLengthEvents
-			}
-			if postIndex > l {
-				return io.ErrUnexpectedEOF
-			}
-			m.PrimarySpAddress = string(dAtA[iNdEx:postIndex])
-			iNdEx = postIndex
 		case 9:
 			if wireType != 0 {
 				return fmt.Errorf("proto: wrong wireType = %d for field PayloadSize", wireType)
@@ -5666,10 +6182,10 @@ func (m *EventCancelCreateObject) Unmarshal(dAtA []byte) error {
 			m.ObjectName = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
 		case 4:
-			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field PrimarySpAddress", wireType)
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field PrimarySpId", wireType)
 			}
-			var stringLen uint64
+			m.PrimarySpId = 0
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowEvents
@@ -5679,24 +6195,11 @@ func (m *EventCancelCreateObject) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				stringLen |= uint64(b&0x7F) << shift
+				m.PrimarySpId |= uint32(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
-			intStringLen := int(stringLen)
-			if intStringLen < 0 {
-				return ErrInvalidLengthEvents
-			}
-			postIndex := iNdEx + intStringLen
-			if postIndex < 0 {
-				return ErrInvalidLengthEvents
-			}
-			if postIndex > l {
-				return io.ErrUnexpectedEOF
-			}
-			m.PrimarySpAddress = string(dAtA[iNdEx:postIndex])
-			iNdEx = postIndex
 		case 6:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field ObjectId", wireType)
@@ -5931,10 +6434,10 @@ func (m *EventSealObject) Unmarshal(dAtA []byte) error {
 				}
 			}
 		case 7:
-			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field SecondarySpAddresses", wireType)
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupId", wireType)
 			}
-			var stringLen uint64
+			m.GlobalVirtualGroupId = 0
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowEvents
@@ -5944,24 +6447,30 @@ func (m *EventSealObject) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				stringLen |= uint64(b&0x7F) << shift
+				m.GlobalVirtualGroupId |= uint32(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
-			intStringLen := int(stringLen)
-			if intStringLen < 0 {
-				return ErrInvalidLengthEvents
-			}
-			postIndex := iNdEx + intStringLen
-			if postIndex < 0 {
-				return ErrInvalidLengthEvents
+		case 8:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field LocalVirtualGroupId", wireType)
 			}
-			if postIndex > l {
-				return io.ErrUnexpectedEOF
+			m.LocalVirtualGroupId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.LocalVirtualGroupId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
 			}
-			m.SecondarySpAddresses = append(m.SecondarySpAddresses, string(dAtA[iNdEx:postIndex]))
-			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipEvents(dAtA[iNdEx:])
@@ -6421,42 +6930,10 @@ func (m *EventDeleteObject) Unmarshal(dAtA []byte) error {
 			}
 			iNdEx = postIndex
 		case 5:
-			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field PrimarySpAddress", wireType)
-			}
-			var stringLen uint64
-			for shift := uint(0); ; shift += 7 {
-				if shift >= 64 {
-					return ErrIntOverflowEvents
-				}
-				if iNdEx >= l {
-					return io.ErrUnexpectedEOF
-				}
-				b := dAtA[iNdEx]
-				iNdEx++
-				stringLen |= uint64(b&0x7F) << shift
-				if b < 0x80 {
-					break
-				}
-			}
-			intStringLen := int(stringLen)
-			if intStringLen < 0 {
-				return ErrInvalidLengthEvents
-			}
-			postIndex := iNdEx + intStringLen
-			if postIndex < 0 {
-				return ErrInvalidLengthEvents
-			}
-			if postIndex > l {
-				return io.ErrUnexpectedEOF
-			}
-			m.PrimarySpAddress = string(dAtA[iNdEx:postIndex])
-			iNdEx = postIndex
-		case 6:
-			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field SecondarySpAddresses", wireType)
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field LocalVirtualGroupId", wireType)
 			}
-			var stringLen uint64
+			m.LocalVirtualGroupId = 0
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowEvents
@@ -6466,24 +6943,11 @@ func (m *EventDeleteObject) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				stringLen |= uint64(b&0x7F) << shift
+				m.LocalVirtualGroupId |= uint32(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
-			intStringLen := int(stringLen)
-			if intStringLen < 0 {
-				return ErrInvalidLengthEvents
-			}
-			postIndex := iNdEx + intStringLen
-			if postIndex < 0 {
-				return ErrInvalidLengthEvents
-			}
-			if postIndex > l {
-				return io.ErrUnexpectedEOF
-			}
-			m.SecondarySpAddresses = append(m.SecondarySpAddresses, string(dAtA[iNdEx:postIndex]))
-			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipEvents(dAtA[iNdEx:])
@@ -8193,6 +8657,25 @@ func (m *EventMirrorBucket) Unmarshal(dAtA []byte) error {
 				return err
 			}
 			iNdEx = postIndex
+		case 5:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field DestChainId", wireType)
+			}
+			m.DestChainId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.DestChainId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
 		default:
 			iNdEx = preIndex
 			skippy, err := skipEvents(dAtA[iNdEx:])
@@ -8328,6 +8811,25 @@ func (m *EventMirrorBucketResult) Unmarshal(dAtA []byte) error {
 				return err
 			}
 			iNdEx = postIndex
+		case 5:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field DestChainId", wireType)
+			}
+			m.DestChainId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.DestChainId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
 		default:
 			iNdEx = preIndex
 			skippy, err := skipEvents(dAtA[iNdEx:])
@@ -8508,6 +9010,25 @@ func (m *EventMirrorObject) Unmarshal(dAtA []byte) error {
 				return err
 			}
 			iNdEx = postIndex
+		case 5:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field DestChainId", wireType)
+			}
+			m.DestChainId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.DestChainId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
 		default:
 			iNdEx = preIndex
 			skippy, err := skipEvents(dAtA[iNdEx:])
@@ -8675,6 +9196,25 @@ func (m *EventMirrorObjectResult) Unmarshal(dAtA []byte) error {
 				return err
 			}
 			iNdEx = postIndex
+		case 5:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field DestChainId", wireType)
+			}
+			m.DestChainId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.DestChainId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
 		default:
 			iNdEx = preIndex
 			skippy, err := skipEvents(dAtA[iNdEx:])
@@ -8823,6 +9363,25 @@ func (m *EventMirrorGroup) Unmarshal(dAtA []byte) error {
 				return err
 			}
 			iNdEx = postIndex
+		case 4:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field DestChainId", wireType)
+			}
+			m.DestChainId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.DestChainId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
 		default:
 			iNdEx = preIndex
 			skippy, err := skipEvents(dAtA[iNdEx:])
@@ -8958,6 +9517,25 @@ func (m *EventMirrorGroupResult) Unmarshal(dAtA []byte) error {
 				return err
 			}
 			iNdEx = postIndex
+		case 4:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field DestChainId", wireType)
+			}
+			m.DestChainId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.DestChainId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
 		default:
 			iNdEx = preIndex
 			skippy, err := skipEvents(dAtA[iNdEx:])
@@ -9084,6 +9662,522 @@ func (m *EventStalePolicyCleanup) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
+func (m *EventMigrationBucket) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowEvents
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: EventMigrationBucket: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: EventMigrationBucket: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Operator", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthEvents
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Operator = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field BucketName", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthEvents
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.BucketName = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field BucketId", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthEvents
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.BucketId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 4:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field DstPrimarySpId", wireType)
+			}
+			m.DstPrimarySpId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.DstPrimarySpId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipEvents(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *EventCancelMigrationBucket) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowEvents
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: EventCancelMigrationBucket: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: EventCancelMigrationBucket: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Operator", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthEvents
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Operator = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field BucketName", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthEvents
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.BucketName = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field BucketId", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthEvents
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.BucketId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipEvents(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *EventCompleteMigrationBucket) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowEvents
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: EventCompleteMigrationBucket: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: EventCompleteMigrationBucket: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Operator", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthEvents
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Operator = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field BucketName", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthEvents
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.BucketName = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field BucketId", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthEvents
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.BucketId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 4:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupFamilyId", wireType)
+			}
+			m.GlobalVirtualGroupFamilyId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.GlobalVirtualGroupFamilyId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 5:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GvgMappings", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthEvents
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.GvgMappings = append(m.GvgMappings, &GVGMapping{})
+			if err := m.GvgMappings[len(m.GvgMappings)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipEvents(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
 func skipEvents(dAtA []byte) (n int, err error) {
 	l := len(dAtA)
 	iNdEx := 0
diff --git a/x/storage/types/expected_keepers.go b/x/storage/types/expected_keepers.go
index 3b60191ea..e3d5ecb79 100644
--- a/x/storage/types/expected_keepers.go
+++ b/x/storage/types/expected_keepers.go
@@ -11,6 +11,7 @@ import (
 	paymenttypes "github.com/bnb-chain/greenfield/x/payment/types"
 	permtypes "github.com/bnb-chain/greenfield/x/permission/types"
 	sptypes "github.com/bnb-chain/greenfield/x/sp/types"
+	"github.com/bnb-chain/greenfield/x/virtualgroup/types"
 )
 
 // AccountKeeper defines the expected account keeper used for simulations (noalias)
@@ -26,18 +27,15 @@ type BankKeeper interface {
 	GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin
 	GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
 	SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
-
 	// Methods imported from bank should be defined here
 }
 
 type SpKeeper interface {
-	GetStorageProvider(ctx sdk.Context, addr sdk.AccAddress) (sp *sptypes.StorageProvider, found bool)
+	GetStorageProvider(ctx sdk.Context, id uint32) (*sptypes.StorageProvider, bool)
+	MustGetStorageProvider(ctx sdk.Context, id uint32) *sptypes.StorageProvider
+	GetStorageProviderByOperatorAddr(ctx sdk.Context, addr sdk.AccAddress) (sp *sptypes.StorageProvider, found bool)
 	GetStorageProviderBySealAddr(ctx sdk.Context, sealAddr sdk.AccAddress) (sp *sptypes.StorageProvider, found bool)
 	GetStorageProviderByGcAddr(ctx sdk.Context, gcAddr sdk.AccAddress) (sp *sptypes.StorageProvider, found bool)
-	IsStorageProviderExistAndInService(ctx sdk.Context, addr sdk.AccAddress) error
-	SetSpStoragePrice(ctx sdk.Context, SpStoragePrice sptypes.SpStoragePrice)
-	SetSecondarySpStorePrice(ctx sdk.Context, secondarySpStorePrice sptypes.SecondarySpStorePrice)
-	GetSpStoragePriceByTime(ctx sdk.Context, spAddr sdk.AccAddress, time int64) (val sptypes.SpStoragePrice, err error)
 }
 
 type PaymentKeeper interface {
@@ -47,8 +45,9 @@ type PaymentKeeper interface {
 	GetStoragePrice(ctx sdk.Context, params paymenttypes.StoragePriceParams) (price paymenttypes.StoragePrice, err error)
 	ApplyUserFlowsList(ctx sdk.Context, userFlows []paymenttypes.UserFlows) (err error)
 	UpdateStreamRecordByAddr(ctx sdk.Context, change *paymenttypes.StreamRecordChange) (ret *paymenttypes.StreamRecord, err error)
-	// GetStreamRecord
 	GetStreamRecord(ctx sdk.Context, account sdk.AccAddress) (val *paymenttypes.StreamRecord, found bool)
+	UpdateStreamRecord(ctx sdk.Context, streamRecord *paymenttypes.StreamRecord, change *paymenttypes.StreamRecordChange) error
+	Withdraw(ctx sdk.Context, fromAddr, toAddr sdk.AccAddress, amount math.Int) error
 }
 
 type PermissionKeeper interface {
@@ -74,9 +73,20 @@ type PermissionKeeper interface {
 }
 
 type CrossChainKeeper interface {
-	CreateRawIBCPackageWithFee(ctx sdk.Context, channelID sdk.ChannelID, packageType sdk.CrossChainPackageType,
+	GetDestBscChainID() sdk.ChainID
+	CreateRawIBCPackageWithFee(ctx sdk.Context, chainID sdk.ChainID, channelID sdk.ChannelID, packageType sdk.CrossChainPackageType,
 		packageLoad []byte, relayerFee *big.Int, ackRelayerFee *big.Int,
 	) (uint64, error)
 
 	RegisterChannel(name string, id sdk.ChannelID, app sdk.CrossChainApplication) error
 }
+
+type VirtualGroupKeeper interface {
+	SetGVG(ctx sdk.Context, gvg *types.GlobalVirtualGroup)
+	GetGVGFamily(ctx sdk.Context, spID, familyID uint32) (*types.GlobalVirtualGroupFamily, bool)
+	GetGVG(ctx sdk.Context, gvgID uint32) (*types.GlobalVirtualGroup, bool)
+	SettleAndDistributeGVGFamily(ctx sdk.Context, sp *sptypes.StorageProvider, family *types.GlobalVirtualGroupFamily) error
+	SettleAndDistributeGVG(ctx sdk.Context, gvg *types.GlobalVirtualGroup) error
+	GetAndCheckGVGFamilyAvailableForNewBucket(ctx sdk.Context, spID, familyID uint32) (*types.GlobalVirtualGroupFamily, error)
+	GetGlobalVirtualGroupIfAvailable(ctx sdk.Context, gvgID uint32, expectedStoreSize uint64) (*types.GlobalVirtualGroup, error)
+}
diff --git a/x/storage/types/expected_keepers_mocks.go b/x/storage/types/expected_keepers_mocks.go
index 3b545f9b3..5ad3b7cdb 100644
--- a/x/storage/types/expected_keepers_mocks.go
+++ b/x/storage/types/expected_keepers_mocks.go
@@ -1,5 +1,5 @@
 // Code generated by MockGen. DO NOT EDIT.
-// Source: expected_keepers.go
+// Source: x/storage/types/expected_keepers.go
 
 // Package types is a generated GoMock package.
 package types
@@ -13,8 +13,9 @@ import (
 	types "github.com/bnb-chain/greenfield/x/payment/types"
 	types0 "github.com/bnb-chain/greenfield/x/permission/types"
 	types1 "github.com/bnb-chain/greenfield/x/sp/types"
-	types2 "github.com/cosmos/cosmos-sdk/types"
-	types3 "github.com/cosmos/cosmos-sdk/x/auth/types"
+	types2 "github.com/bnb-chain/greenfield/x/virtualgroup/types"
+	types3 "github.com/cosmos/cosmos-sdk/types"
+	types4 "github.com/cosmos/cosmos-sdk/x/auth/types"
 	gomock "github.com/golang/mock/gomock"
 )
 
@@ -42,10 +43,10 @@ func (m *MockAccountKeeper) EXPECT() *MockAccountKeeperMockRecorder {
 }
 
 // GetAccount mocks base method.
-func (m *MockAccountKeeper) GetAccount(ctx types2.Context, addr types2.AccAddress) types3.AccountI {
+func (m *MockAccountKeeper) GetAccount(ctx types3.Context, addr types3.AccAddress) types4.AccountI {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "GetAccount", ctx, addr)
-	ret0, _ := ret[0].(types3.AccountI)
+	ret0, _ := ret[0].(types4.AccountI)
 	return ret0
 }
 
@@ -56,10 +57,10 @@ func (mr *MockAccountKeeperMockRecorder) GetAccount(ctx, addr interface{}) *gomo
 }
 
 // GetModuleAddress mocks base method.
-func (m *MockAccountKeeper) GetModuleAddress(name string) types2.AccAddress {
+func (m *MockAccountKeeper) GetModuleAddress(name string) types3.AccAddress {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "GetModuleAddress", name)
-	ret0, _ := ret[0].(types2.AccAddress)
+	ret0, _ := ret[0].(types3.AccAddress)
 	return ret0
 }
 
@@ -93,10 +94,10 @@ func (m *MockBankKeeper) EXPECT() *MockBankKeeperMockRecorder {
 }
 
 // GetAllBalances mocks base method.
-func (m *MockBankKeeper) GetAllBalances(ctx types2.Context, addr types2.AccAddress) types2.Coins {
+func (m *MockBankKeeper) GetAllBalances(ctx types3.Context, addr types3.AccAddress) types3.Coins {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "GetAllBalances", ctx, addr)
-	ret0, _ := ret[0].(types2.Coins)
+	ret0, _ := ret[0].(types3.Coins)
 	return ret0
 }
 
@@ -107,10 +108,10 @@ func (mr *MockBankKeeperMockRecorder) GetAllBalances(ctx, addr interface{}) *gom
 }
 
 // GetBalance mocks base method.
-func (m *MockBankKeeper) GetBalance(ctx types2.Context, addr types2.AccAddress, denom string) types2.Coin {
+func (m *MockBankKeeper) GetBalance(ctx types3.Context, addr types3.AccAddress, denom string) types3.Coin {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "GetBalance", ctx, addr, denom)
-	ret0, _ := ret[0].(types2.Coin)
+	ret0, _ := ret[0].(types3.Coin)
 	return ret0
 }
 
@@ -121,7 +122,7 @@ func (mr *MockBankKeeperMockRecorder) GetBalance(ctx, addr, denom interface{}) *
 }
 
 // SendCoinsFromModuleToAccount mocks base method.
-func (m *MockBankKeeper) SendCoinsFromModuleToAccount(ctx types2.Context, senderModule string, recipientAddr types2.AccAddress, amt types2.Coins) error {
+func (m *MockBankKeeper) SendCoinsFromModuleToAccount(ctx types3.Context, senderModule string, recipientAddr types3.AccAddress, amt types3.Coins) error {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "SendCoinsFromModuleToAccount", ctx, senderModule, recipientAddr, amt)
 	ret0, _ := ret[0].(error)
@@ -135,10 +136,10 @@ func (mr *MockBankKeeperMockRecorder) SendCoinsFromModuleToAccount(ctx, senderMo
 }
 
 // SpendableCoins mocks base method.
-func (m *MockBankKeeper) SpendableCoins(ctx types2.Context, addr types2.AccAddress) types2.Coins {
+func (m *MockBankKeeper) SpendableCoins(ctx types3.Context, addr types3.AccAddress) types3.Coins {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "SpendableCoins", ctx, addr)
-	ret0, _ := ret[0].(types2.Coins)
+	ret0, _ := ret[0].(types3.Coins)
 	return ret0
 }
 
@@ -171,38 +172,23 @@ func (m *MockSpKeeper) EXPECT() *MockSpKeeperMockRecorder {
 	return m.recorder
 }
 
-// GetSpStoragePriceByTime mocks base method.
-func (m *MockSpKeeper) GetSpStoragePriceByTime(ctx types2.Context, spAddr types2.AccAddress, time int64) (types1.SpStoragePrice, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetSpStoragePriceByTime", ctx, spAddr, time)
-	ret0, _ := ret[0].(types1.SpStoragePrice)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// GetSpStoragePriceByTime indicates an expected call of GetSpStoragePriceByTime.
-func (mr *MockSpKeeperMockRecorder) GetSpStoragePriceByTime(ctx, spAddr, time interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSpStoragePriceByTime", reflect.TypeOf((*MockSpKeeper)(nil).GetSpStoragePriceByTime), ctx, spAddr, time)
-}
-
 // GetStorageProvider mocks base method.
-func (m *MockSpKeeper) GetStorageProvider(ctx types2.Context, addr types2.AccAddress) (*types1.StorageProvider, bool) {
+func (m *MockSpKeeper) GetStorageProvider(ctx types3.Context, id uint32) (*types1.StorageProvider, bool) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetStorageProvider", ctx, addr)
+	ret := m.ctrl.Call(m, "GetStorageProvider", ctx, id)
 	ret0, _ := ret[0].(*types1.StorageProvider)
 	ret1, _ := ret[1].(bool)
 	return ret0, ret1
 }
 
 // GetStorageProvider indicates an expected call of GetStorageProvider.
-func (mr *MockSpKeeperMockRecorder) GetStorageProvider(ctx, addr interface{}) *gomock.Call {
+func (mr *MockSpKeeperMockRecorder) GetStorageProvider(ctx, id interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageProvider", reflect.TypeOf((*MockSpKeeper)(nil).GetStorageProvider), ctx, addr)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageProvider", reflect.TypeOf((*MockSpKeeper)(nil).GetStorageProvider), ctx, id)
 }
 
 // GetStorageProviderByGcAddr mocks base method.
-func (m *MockSpKeeper) GetStorageProviderByGcAddr(ctx types2.Context, gcAddr types2.AccAddress) (*types1.StorageProvider, bool) {
+func (m *MockSpKeeper) GetStorageProviderByGcAddr(ctx types3.Context, gcAddr types3.AccAddress) (*types1.StorageProvider, bool) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "GetStorageProviderByGcAddr", ctx, gcAddr)
 	ret0, _ := ret[0].(*types1.StorageProvider)
@@ -216,57 +202,48 @@ func (mr *MockSpKeeperMockRecorder) GetStorageProviderByGcAddr(ctx, gcAddr inter
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageProviderByGcAddr", reflect.TypeOf((*MockSpKeeper)(nil).GetStorageProviderByGcAddr), ctx, gcAddr)
 }
 
-// GetStorageProviderBySealAddr mocks base method.
-func (m *MockSpKeeper) GetStorageProviderBySealAddr(ctx types2.Context, sealAddr types2.AccAddress) (*types1.StorageProvider, bool) {
+// GetStorageProviderByOperatorAddr mocks base method.
+func (m *MockSpKeeper) GetStorageProviderByOperatorAddr(ctx types3.Context, addr types3.AccAddress) (*types1.StorageProvider, bool) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetStorageProviderBySealAddr", ctx, sealAddr)
+	ret := m.ctrl.Call(m, "GetStorageProviderByOperatorAddr", ctx, addr)
 	ret0, _ := ret[0].(*types1.StorageProvider)
 	ret1, _ := ret[1].(bool)
 	return ret0, ret1
 }
 
-// GetStorageProviderBySealAddr indicates an expected call of GetStorageProviderBySealAddr.
-func (mr *MockSpKeeperMockRecorder) GetStorageProviderBySealAddr(ctx, sealAddr interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageProviderBySealAddr", reflect.TypeOf((*MockSpKeeper)(nil).GetStorageProviderBySealAddr), ctx, sealAddr)
-}
-
-// IsStorageProviderExistAndInService mocks base method.
-func (m *MockSpKeeper) IsStorageProviderExistAndInService(ctx types2.Context, addr types2.AccAddress) error {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "IsStorageProviderExistAndInService", ctx, addr)
-	ret0, _ := ret[0].(error)
-	return ret0
-}
-
-// IsStorageProviderExistAndInService indicates an expected call of IsStorageProviderExistAndInService.
-func (mr *MockSpKeeperMockRecorder) IsStorageProviderExistAndInService(ctx, addr interface{}) *gomock.Call {
+// GetStorageProviderByOperatorAddr indicates an expected call of GetStorageProviderByOperatorAddr.
+func (mr *MockSpKeeperMockRecorder) GetStorageProviderByOperatorAddr(ctx, addr interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsStorageProviderExistAndInService", reflect.TypeOf((*MockSpKeeper)(nil).IsStorageProviderExistAndInService), ctx, addr)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageProviderByOperatorAddr", reflect.TypeOf((*MockSpKeeper)(nil).GetStorageProviderByOperatorAddr), ctx, addr)
 }
 
-// SetSecondarySpStorePrice mocks base method.
-func (m *MockSpKeeper) SetSecondarySpStorePrice(ctx types2.Context, secondarySpStorePrice types1.SecondarySpStorePrice) {
+// GetStorageProviderBySealAddr mocks base method.
+func (m *MockSpKeeper) GetStorageProviderBySealAddr(ctx types3.Context, sealAddr types3.AccAddress) (*types1.StorageProvider, bool) {
 	m.ctrl.T.Helper()
-	m.ctrl.Call(m, "SetSecondarySpStorePrice", ctx, secondarySpStorePrice)
+	ret := m.ctrl.Call(m, "GetStorageProviderBySealAddr", ctx, sealAddr)
+	ret0, _ := ret[0].(*types1.StorageProvider)
+	ret1, _ := ret[1].(bool)
+	return ret0, ret1
 }
 
-// SetSecondarySpStorePrice indicates an expected call of SetSecondarySpStorePrice.
-func (mr *MockSpKeeperMockRecorder) SetSecondarySpStorePrice(ctx, secondarySpStorePrice interface{}) *gomock.Call {
+// GetStorageProviderBySealAddr indicates an expected call of GetStorageProviderBySealAddr.
+func (mr *MockSpKeeperMockRecorder) GetStorageProviderBySealAddr(ctx, sealAddr interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSecondarySpStorePrice", reflect.TypeOf((*MockSpKeeper)(nil).SetSecondarySpStorePrice), ctx, secondarySpStorePrice)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageProviderBySealAddr", reflect.TypeOf((*MockSpKeeper)(nil).GetStorageProviderBySealAddr), ctx, sealAddr)
 }
 
-// SetSpStoragePrice mocks base method.
-func (m *MockSpKeeper) SetSpStoragePrice(ctx types2.Context, SpStoragePrice types1.SpStoragePrice) {
+// MustGetStorageProvider mocks base method.
+func (m *MockSpKeeper) MustGetStorageProvider(ctx types3.Context, id uint32) *types1.StorageProvider {
 	m.ctrl.T.Helper()
-	m.ctrl.Call(m, "SetSpStoragePrice", ctx, SpStoragePrice)
+	ret := m.ctrl.Call(m, "MustGetStorageProvider", ctx, id)
+	ret0, _ := ret[0].(*types1.StorageProvider)
+	return ret0
 }
 
-// SetSpStoragePrice indicates an expected call of SetSpStoragePrice.
-func (mr *MockSpKeeperMockRecorder) SetSpStoragePrice(ctx, SpStoragePrice interface{}) *gomock.Call {
+// MustGetStorageProvider indicates an expected call of MustGetStorageProvider.
+func (mr *MockSpKeeperMockRecorder) MustGetStorageProvider(ctx, id interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSpStoragePrice", reflect.TypeOf((*MockSpKeeper)(nil).SetSpStoragePrice), ctx, SpStoragePrice)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MustGetStorageProvider", reflect.TypeOf((*MockSpKeeper)(nil).MustGetStorageProvider), ctx, id)
 }
 
 // MockPaymentKeeper is a mock of PaymentKeeper interface.
@@ -293,7 +270,7 @@ func (m *MockPaymentKeeper) EXPECT() *MockPaymentKeeperMockRecorder {
 }
 
 // ApplyUserFlowsList mocks base method.
-func (m *MockPaymentKeeper) ApplyUserFlowsList(ctx types2.Context, userFlows []types.UserFlows) error {
+func (m *MockPaymentKeeper) ApplyUserFlowsList(ctx types3.Context, userFlows []types.UserFlows) error {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "ApplyUserFlowsList", ctx, userFlows)
 	ret0, _ := ret[0].(error)
@@ -307,7 +284,7 @@ func (mr *MockPaymentKeeperMockRecorder) ApplyUserFlowsList(ctx, userFlows inter
 }
 
 // GetParams mocks base method.
-func (m *MockPaymentKeeper) GetParams(ctx types2.Context) types.Params {
+func (m *MockPaymentKeeper) GetParams(ctx types3.Context) types.Params {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "GetParams", ctx)
 	ret0, _ := ret[0].(types.Params)
@@ -321,7 +298,7 @@ func (mr *MockPaymentKeeperMockRecorder) GetParams(ctx interface{}) *gomock.Call
 }
 
 // GetStoragePrice mocks base method.
-func (m *MockPaymentKeeper) GetStoragePrice(ctx types2.Context, params types.StoragePriceParams) (types.StoragePrice, error) {
+func (m *MockPaymentKeeper) GetStoragePrice(ctx types3.Context, params types.StoragePriceParams) (types.StoragePrice, error) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "GetStoragePrice", ctx, params)
 	ret0, _ := ret[0].(types.StoragePrice)
@@ -336,7 +313,7 @@ func (mr *MockPaymentKeeperMockRecorder) GetStoragePrice(ctx, params interface{}
 }
 
 // GetStreamRecord mocks base method.
-func (m *MockPaymentKeeper) GetStreamRecord(ctx types2.Context, account types2.AccAddress) (*types.StreamRecord, bool) {
+func (m *MockPaymentKeeper) GetStreamRecord(ctx types3.Context, account types3.AccAddress) (*types.StreamRecord, bool) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "GetStreamRecord", ctx, account)
 	ret0, _ := ret[0].(*types.StreamRecord)
@@ -351,7 +328,7 @@ func (mr *MockPaymentKeeperMockRecorder) GetStreamRecord(ctx, account interface{
 }
 
 // GetVersionedParamsWithTs mocks base method.
-func (m *MockPaymentKeeper) GetVersionedParamsWithTs(ctx types2.Context, time int64) (types.VersionedParams, error) {
+func (m *MockPaymentKeeper) GetVersionedParamsWithTs(ctx types3.Context, time int64) (types.VersionedParams, error) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "GetVersionedParamsWithTs", ctx, time)
 	ret0, _ := ret[0].(types.VersionedParams)
@@ -366,7 +343,7 @@ func (mr *MockPaymentKeeperMockRecorder) GetVersionedParamsWithTs(ctx, time inte
 }
 
 // IsPaymentAccountOwner mocks base method.
-func (m *MockPaymentKeeper) IsPaymentAccountOwner(ctx types2.Context, addr, owner types2.AccAddress) bool {
+func (m *MockPaymentKeeper) IsPaymentAccountOwner(ctx types3.Context, addr, owner types3.AccAddress) bool {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "IsPaymentAccountOwner", ctx, addr, owner)
 	ret0, _ := ret[0].(bool)
@@ -379,8 +356,22 @@ func (mr *MockPaymentKeeperMockRecorder) IsPaymentAccountOwner(ctx, addr, owner
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsPaymentAccountOwner", reflect.TypeOf((*MockPaymentKeeper)(nil).IsPaymentAccountOwner), ctx, addr, owner)
 }
 
+// UpdateStreamRecord mocks base method.
+func (m *MockPaymentKeeper) UpdateStreamRecord(ctx types3.Context, streamRecord *types.StreamRecord, change *types.StreamRecordChange) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "UpdateStreamRecord", ctx, streamRecord, change)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// UpdateStreamRecord indicates an expected call of UpdateStreamRecord.
+func (mr *MockPaymentKeeperMockRecorder) UpdateStreamRecord(ctx, streamRecord, change interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateStreamRecord", reflect.TypeOf((*MockPaymentKeeper)(nil).UpdateStreamRecord), ctx, streamRecord, change)
+}
+
 // UpdateStreamRecordByAddr mocks base method.
-func (m *MockPaymentKeeper) UpdateStreamRecordByAddr(ctx types2.Context, change *types.StreamRecordChange) (*types.StreamRecord, error) {
+func (m *MockPaymentKeeper) UpdateStreamRecordByAddr(ctx types3.Context, change *types.StreamRecordChange) (*types.StreamRecord, error) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "UpdateStreamRecordByAddr", ctx, change)
 	ret0, _ := ret[0].(*types.StreamRecord)
@@ -394,6 +385,20 @@ func (mr *MockPaymentKeeperMockRecorder) UpdateStreamRecordByAddr(ctx, change in
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateStreamRecordByAddr", reflect.TypeOf((*MockPaymentKeeper)(nil).UpdateStreamRecordByAddr), ctx, change)
 }
 
+// Withdraw mocks base method.
+func (m *MockPaymentKeeper) Withdraw(ctx types3.Context, fromAddr, toAddr types3.AccAddress, amount math.Int) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Withdraw", ctx, fromAddr, toAddr, amount)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// Withdraw indicates an expected call of Withdraw.
+func (mr *MockPaymentKeeperMockRecorder) Withdraw(ctx, fromAddr, toAddr, amount interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Withdraw", reflect.TypeOf((*MockPaymentKeeper)(nil).Withdraw), ctx, fromAddr, toAddr, amount)
+}
+
 // MockPermissionKeeper is a mock of PermissionKeeper interface.
 type MockPermissionKeeper struct {
 	ctrl     *gomock.Controller
@@ -418,7 +423,7 @@ func (m *MockPermissionKeeper) EXPECT() *MockPermissionKeeperMockRecorder {
 }
 
 // AddGroupMember mocks base method.
-func (m *MockPermissionKeeper) AddGroupMember(ctx types2.Context, groupID math.Uint, member types2.AccAddress) error {
+func (m *MockPermissionKeeper) AddGroupMember(ctx types3.Context, groupID math.Uint, member types3.AccAddress) error {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "AddGroupMember", ctx, groupID, member)
 	ret0, _ := ret[0].(error)
@@ -432,7 +437,7 @@ func (mr *MockPermissionKeeperMockRecorder) AddGroupMember(ctx, groupID, member
 }
 
 // DeletePolicy mocks base method.
-func (m *MockPermissionKeeper) DeletePolicy(ctx types2.Context, principal *types0.Principal, resourceType resource.ResourceType, resourceID math.Uint) (math.Uint, error) {
+func (m *MockPermissionKeeper) DeletePolicy(ctx types3.Context, principal *types0.Principal, resourceType resource.ResourceType, resourceID math.Uint) (math.Uint, error) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "DeletePolicy", ctx, principal, resourceType, resourceID)
 	ret0, _ := ret[0].(math.Uint)
@@ -447,7 +452,7 @@ func (mr *MockPermissionKeeperMockRecorder) DeletePolicy(ctx, principal, resourc
 }
 
 // ExistAccountPolicyForResource mocks base method.
-func (m *MockPermissionKeeper) ExistAccountPolicyForResource(ctx types2.Context, resourceType resource.ResourceType, resourceID math.Uint) bool {
+func (m *MockPermissionKeeper) ExistAccountPolicyForResource(ctx types3.Context, resourceType resource.ResourceType, resourceID math.Uint) bool {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "ExistAccountPolicyForResource", ctx, resourceType, resourceID)
 	ret0, _ := ret[0].(bool)
@@ -461,7 +466,7 @@ func (mr *MockPermissionKeeperMockRecorder) ExistAccountPolicyForResource(ctx, r
 }
 
 // ExistGroupMemberForGroup mocks base method.
-func (m *MockPermissionKeeper) ExistGroupMemberForGroup(ctx types2.Context, groupId math.Uint) bool {
+func (m *MockPermissionKeeper) ExistGroupMemberForGroup(ctx types3.Context, groupId math.Uint) bool {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "ExistGroupMemberForGroup", ctx, groupId)
 	ret0, _ := ret[0].(bool)
@@ -475,7 +480,7 @@ func (mr *MockPermissionKeeperMockRecorder) ExistGroupMemberForGroup(ctx, groupI
 }
 
 // ExistGroupPolicyForResource mocks base method.
-func (m *MockPermissionKeeper) ExistGroupPolicyForResource(ctx types2.Context, resourceType resource.ResourceType, resourceID math.Uint) bool {
+func (m *MockPermissionKeeper) ExistGroupPolicyForResource(ctx types3.Context, resourceType resource.ResourceType, resourceID math.Uint) bool {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "ExistGroupPolicyForResource", ctx, resourceType, resourceID)
 	ret0, _ := ret[0].(bool)
@@ -489,7 +494,7 @@ func (mr *MockPermissionKeeperMockRecorder) ExistGroupPolicyForResource(ctx, res
 }
 
 // ForceDeleteAccountPolicyForResource mocks base method.
-func (m *MockPermissionKeeper) ForceDeleteAccountPolicyForResource(ctx types2.Context, maxDelete, deletedCount uint64, resourceType resource.ResourceType, resourceID math.Uint) (uint64, bool) {
+func (m *MockPermissionKeeper) ForceDeleteAccountPolicyForResource(ctx types3.Context, maxDelete, deletedCount uint64, resourceType resource.ResourceType, resourceID math.Uint) (uint64, bool) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "ForceDeleteAccountPolicyForResource", ctx, maxDelete, deletedCount, resourceType, resourceID)
 	ret0, _ := ret[0].(uint64)
@@ -504,7 +509,7 @@ func (mr *MockPermissionKeeperMockRecorder) ForceDeleteAccountPolicyForResource(
 }
 
 // ForceDeleteGroupMembers mocks base method.
-func (m *MockPermissionKeeper) ForceDeleteGroupMembers(ctx types2.Context, maxDelete, deletedTotal uint64, groupId math.Uint) (uint64, bool) {
+func (m *MockPermissionKeeper) ForceDeleteGroupMembers(ctx types3.Context, maxDelete, deletedTotal uint64, groupId math.Uint) (uint64, bool) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "ForceDeleteGroupMembers", ctx, maxDelete, deletedTotal, groupId)
 	ret0, _ := ret[0].(uint64)
@@ -519,7 +524,7 @@ func (mr *MockPermissionKeeperMockRecorder) ForceDeleteGroupMembers(ctx, maxDele
 }
 
 // ForceDeleteGroupPolicyForResource mocks base method.
-func (m *MockPermissionKeeper) ForceDeleteGroupPolicyForResource(ctx types2.Context, maxDelete, deletedCount uint64, resourceType resource.ResourceType, resourceID math.Uint) (uint64, bool) {
+func (m *MockPermissionKeeper) ForceDeleteGroupPolicyForResource(ctx types3.Context, maxDelete, deletedCount uint64, resourceType resource.ResourceType, resourceID math.Uint) (uint64, bool) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "ForceDeleteGroupPolicyForResource", ctx, maxDelete, deletedCount, resourceType, resourceID)
 	ret0, _ := ret[0].(uint64)
@@ -534,7 +539,7 @@ func (mr *MockPermissionKeeperMockRecorder) ForceDeleteGroupPolicyForResource(ct
 }
 
 // GetGroupMember mocks base method.
-func (m *MockPermissionKeeper) GetGroupMember(ctx types2.Context, groupID math.Uint, member types2.AccAddress) (*types0.GroupMember, bool) {
+func (m *MockPermissionKeeper) GetGroupMember(ctx types3.Context, groupID math.Uint, member types3.AccAddress) (*types0.GroupMember, bool) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "GetGroupMember", ctx, groupID, member)
 	ret0, _ := ret[0].(*types0.GroupMember)
@@ -549,7 +554,7 @@ func (mr *MockPermissionKeeperMockRecorder) GetGroupMember(ctx, groupID, member
 }
 
 // GetGroupMemberByID mocks base method.
-func (m *MockPermissionKeeper) GetGroupMemberByID(ctx types2.Context, groupMemberID math.Uint) (*types0.GroupMember, bool) {
+func (m *MockPermissionKeeper) GetGroupMemberByID(ctx types3.Context, groupMemberID math.Uint) (*types0.GroupMember, bool) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "GetGroupMemberByID", ctx, groupMemberID)
 	ret0, _ := ret[0].(*types0.GroupMember)
@@ -564,7 +569,7 @@ func (mr *MockPermissionKeeperMockRecorder) GetGroupMemberByID(ctx, groupMemberI
 }
 
 // GetPolicyByID mocks base method.
-func (m *MockPermissionKeeper) GetPolicyByID(ctx types2.Context, policyID math.Uint) (*types0.Policy, bool) {
+func (m *MockPermissionKeeper) GetPolicyByID(ctx types3.Context, policyID math.Uint) (*types0.Policy, bool) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "GetPolicyByID", ctx, policyID)
 	ret0, _ := ret[0].(*types0.Policy)
@@ -579,7 +584,7 @@ func (mr *MockPermissionKeeperMockRecorder) GetPolicyByID(ctx, policyID interfac
 }
 
 // GetPolicyForAccount mocks base method.
-func (m *MockPermissionKeeper) GetPolicyForAccount(ctx types2.Context, resourceID math.Uint, resourceType resource.ResourceType, addr types2.AccAddress) (*types0.Policy, bool) {
+func (m *MockPermissionKeeper) GetPolicyForAccount(ctx types3.Context, resourceID math.Uint, resourceType resource.ResourceType, addr types3.AccAddress) (*types0.Policy, bool) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "GetPolicyForAccount", ctx, resourceID, resourceType, addr)
 	ret0, _ := ret[0].(*types0.Policy)
@@ -594,7 +599,7 @@ func (mr *MockPermissionKeeperMockRecorder) GetPolicyForAccount(ctx, resourceID,
 }
 
 // GetPolicyForGroup mocks base method.
-func (m *MockPermissionKeeper) GetPolicyForGroup(ctx types2.Context, resourceID math.Uint, resourceType resource.ResourceType, groupID math.Uint) (*types0.Policy, bool) {
+func (m *MockPermissionKeeper) GetPolicyForGroup(ctx types3.Context, resourceID math.Uint, resourceType resource.ResourceType, groupID math.Uint) (*types0.Policy, bool) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "GetPolicyForGroup", ctx, resourceID, resourceType, groupID)
 	ret0, _ := ret[0].(*types0.Policy)
@@ -609,7 +614,7 @@ func (mr *MockPermissionKeeperMockRecorder) GetPolicyForGroup(ctx, resourceID, r
 }
 
 // PutPolicy mocks base method.
-func (m *MockPermissionKeeper) PutPolicy(ctx types2.Context, policy *types0.Policy) (math.Uint, error) {
+func (m *MockPermissionKeeper) PutPolicy(ctx types3.Context, policy *types0.Policy) (math.Uint, error) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "PutPolicy", ctx, policy)
 	ret0, _ := ret[0].(math.Uint)
@@ -624,7 +629,7 @@ func (mr *MockPermissionKeeperMockRecorder) PutPolicy(ctx, policy interface{}) *
 }
 
 // RemoveGroupMember mocks base method.
-func (m *MockPermissionKeeper) RemoveGroupMember(ctx types2.Context, groupID math.Uint, member types2.AccAddress) error {
+func (m *MockPermissionKeeper) RemoveGroupMember(ctx types3.Context, groupID math.Uint, member types3.AccAddress) error {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "RemoveGroupMember", ctx, groupID, member)
 	ret0, _ := ret[0].(error)
@@ -638,7 +643,7 @@ func (mr *MockPermissionKeeperMockRecorder) RemoveGroupMember(ctx, groupID, memb
 }
 
 // VerifyPolicy mocks base method.
-func (m *MockPermissionKeeper) VerifyPolicy(ctx types2.Context, resourceID math.Uint, resourceType resource.ResourceType, operator types2.AccAddress, action types0.ActionType, opts *types0.VerifyOptions) types0.Effect {
+func (m *MockPermissionKeeper) VerifyPolicy(ctx types3.Context, resourceID math.Uint, resourceType resource.ResourceType, operator types3.AccAddress, action types0.ActionType, opts *types0.VerifyOptions) types0.Effect {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "VerifyPolicy", ctx, resourceID, resourceType, operator, action, opts)
 	ret0, _ := ret[0].(types0.Effect)
@@ -675,22 +680,36 @@ func (m *MockCrossChainKeeper) EXPECT() *MockCrossChainKeeperMockRecorder {
 }
 
 // CreateRawIBCPackageWithFee mocks base method.
-func (m *MockCrossChainKeeper) CreateRawIBCPackageWithFee(ctx types2.Context, channelID types2.ChannelID, packageType types2.CrossChainPackageType, packageLoad []byte, relayerFee, ackRelayerFee *big.Int) (uint64, error) {
+func (m *MockCrossChainKeeper) CreateRawIBCPackageWithFee(ctx types3.Context, chainID types3.ChainID, channelID types3.ChannelID, packageType types3.CrossChainPackageType, packageLoad []byte, relayerFee, ackRelayerFee *big.Int) (uint64, error) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "CreateRawIBCPackageWithFee", ctx, channelID, packageType, packageLoad, relayerFee, ackRelayerFee)
+	ret := m.ctrl.Call(m, "CreateRawIBCPackageWithFee", ctx, chainID, channelID, packageType, packageLoad, relayerFee, ackRelayerFee)
 	ret0, _ := ret[0].(uint64)
 	ret1, _ := ret[1].(error)
 	return ret0, ret1
 }
 
 // CreateRawIBCPackageWithFee indicates an expected call of CreateRawIBCPackageWithFee.
-func (mr *MockCrossChainKeeperMockRecorder) CreateRawIBCPackageWithFee(ctx, channelID, packageType, packageLoad, relayerFee, ackRelayerFee interface{}) *gomock.Call {
+func (mr *MockCrossChainKeeperMockRecorder) CreateRawIBCPackageWithFee(ctx, chainID, channelID, packageType, packageLoad, relayerFee, ackRelayerFee interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateRawIBCPackageWithFee", reflect.TypeOf((*MockCrossChainKeeper)(nil).CreateRawIBCPackageWithFee), ctx, channelID, packageType, packageLoad, relayerFee, ackRelayerFee)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateRawIBCPackageWithFee", reflect.TypeOf((*MockCrossChainKeeper)(nil).CreateRawIBCPackageWithFee), ctx, chainID, channelID, packageType, packageLoad, relayerFee, ackRelayerFee)
+}
+
+// GetDestBscChainID mocks base method.
+func (m *MockCrossChainKeeper) GetDestBscChainID() types3.ChainID {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetDestBscChainID")
+	ret0, _ := ret[0].(types3.ChainID)
+	return ret0
+}
+
+// GetDestBscChainID indicates an expected call of GetDestBscChainID.
+func (mr *MockCrossChainKeeperMockRecorder) GetDestBscChainID() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDestBscChainID", reflect.TypeOf((*MockCrossChainKeeper)(nil).GetDestBscChainID))
 }
 
 // RegisterChannel mocks base method.
-func (m *MockCrossChainKeeper) RegisterChannel(name string, id types2.ChannelID, app types2.CrossChainApplication) error {
+func (m *MockCrossChainKeeper) RegisterChannel(name string, id types3.ChannelID, app types3.CrossChainApplication) error {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "RegisterChannel", name, id, app)
 	ret0, _ := ret[0].(error)
@@ -702,3 +721,126 @@ func (mr *MockCrossChainKeeperMockRecorder) RegisterChannel(name, id, app interf
 	mr.mock.ctrl.T.Helper()
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterChannel", reflect.TypeOf((*MockCrossChainKeeper)(nil).RegisterChannel), name, id, app)
 }
+
+// MockVirtualGroupKeeper is a mock of VirtualGroupKeeper interface.
+type MockVirtualGroupKeeper struct {
+	ctrl     *gomock.Controller
+	recorder *MockVirtualGroupKeeperMockRecorder
+}
+
+// MockVirtualGroupKeeperMockRecorder is the mock recorder for MockVirtualGroupKeeper.
+type MockVirtualGroupKeeperMockRecorder struct {
+	mock *MockVirtualGroupKeeper
+}
+
+// NewMockVirtualGroupKeeper creates a new mock instance.
+func NewMockVirtualGroupKeeper(ctrl *gomock.Controller) *MockVirtualGroupKeeper {
+	mock := &MockVirtualGroupKeeper{ctrl: ctrl}
+	mock.recorder = &MockVirtualGroupKeeperMockRecorder{mock}
+	return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockVirtualGroupKeeper) EXPECT() *MockVirtualGroupKeeperMockRecorder {
+	return m.recorder
+}
+
+// GetAndCheckGVGFamilyAvailableForNewBucket mocks base method.
+func (m *MockVirtualGroupKeeper) GetAndCheckGVGFamilyAvailableForNewBucket(ctx types3.Context, spID, familyID uint32) (*types2.GlobalVirtualGroupFamily, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetAndCheckGVGFamilyAvailableForNewBucket", ctx, spID, familyID)
+	ret0, _ := ret[0].(*types2.GlobalVirtualGroupFamily)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// GetAndCheckGVGFamilyAvailableForNewBucket indicates an expected call of GetAndCheckGVGFamilyAvailableForNewBucket.
+func (mr *MockVirtualGroupKeeperMockRecorder) GetAndCheckGVGFamilyAvailableForNewBucket(ctx, spID, familyID interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAndCheckGVGFamilyAvailableForNewBucket", reflect.TypeOf((*MockVirtualGroupKeeper)(nil).GetAndCheckGVGFamilyAvailableForNewBucket), ctx, spID, familyID)
+}
+
+// GetGVG mocks base method.
+func (m *MockVirtualGroupKeeper) GetGVG(ctx types3.Context, gvgID uint32) (*types2.GlobalVirtualGroup, bool) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetGVG", ctx, gvgID)
+	ret0, _ := ret[0].(*types2.GlobalVirtualGroup)
+	ret1, _ := ret[1].(bool)
+	return ret0, ret1
+}
+
+// GetGVG indicates an expected call of GetGVG.
+func (mr *MockVirtualGroupKeeperMockRecorder) GetGVG(ctx, gvgID interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGVG", reflect.TypeOf((*MockVirtualGroupKeeper)(nil).GetGVG), ctx, gvgID)
+}
+
+// GetGVGFamily mocks base method.
+func (m *MockVirtualGroupKeeper) GetGVGFamily(ctx types3.Context, spID, familyID uint32) (*types2.GlobalVirtualGroupFamily, bool) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetGVGFamily", ctx, spID, familyID)
+	ret0, _ := ret[0].(*types2.GlobalVirtualGroupFamily)
+	ret1, _ := ret[1].(bool)
+	return ret0, ret1
+}
+
+// GetGVGFamily indicates an expected call of GetGVGFamily.
+func (mr *MockVirtualGroupKeeperMockRecorder) GetGVGFamily(ctx, spID, familyID interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGVGFamily", reflect.TypeOf((*MockVirtualGroupKeeper)(nil).GetGVGFamily), ctx, spID, familyID)
+}
+
+// GetGlobalVirtualGroupIfAvailable mocks base method.
+func (m *MockVirtualGroupKeeper) GetGlobalVirtualGroupIfAvailable(ctx types3.Context, gvgID uint32, expectedStoreSize uint64) (*types2.GlobalVirtualGroup, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetGlobalVirtualGroupIfAvailable", ctx, gvgID, expectedStoreSize)
+	ret0, _ := ret[0].(*types2.GlobalVirtualGroup)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// GetGlobalVirtualGroupIfAvailable indicates an expected call of GetGlobalVirtualGroupIfAvailable.
+func (mr *MockVirtualGroupKeeperMockRecorder) GetGlobalVirtualGroupIfAvailable(ctx, gvgID, expectedStoreSize interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGlobalVirtualGroupIfAvailable", reflect.TypeOf((*MockVirtualGroupKeeper)(nil).GetGlobalVirtualGroupIfAvailable), ctx, gvgID, expectedStoreSize)
+}
+
+// SetGVG mocks base method.
+func (m *MockVirtualGroupKeeper) SetGVG(ctx types3.Context, gvg *types2.GlobalVirtualGroup) {
+	m.ctrl.T.Helper()
+	m.ctrl.Call(m, "SetGVG", ctx, gvg)
+}
+
+// SetGVG indicates an expected call of SetGVG.
+func (mr *MockVirtualGroupKeeperMockRecorder) SetGVG(ctx, gvg interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetGVG", reflect.TypeOf((*MockVirtualGroupKeeper)(nil).SetGVG), ctx, gvg)
+}
+
+// SettleAndDistributeGVG mocks base method.
+func (m *MockVirtualGroupKeeper) SettleAndDistributeGVG(ctx types3.Context, gvg *types2.GlobalVirtualGroup) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "SettleAndDistributeGVG", ctx, gvg)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// SettleAndDistributeGVG indicates an expected call of SettleAndDistributeGVG.
+func (mr *MockVirtualGroupKeeperMockRecorder) SettleAndDistributeGVG(ctx, gvg interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SettleAndDistributeGVG", reflect.TypeOf((*MockVirtualGroupKeeper)(nil).SettleAndDistributeGVG), ctx, gvg)
+}
+
+// SettleAndDistributeGVGFamily mocks base method.
+func (m *MockVirtualGroupKeeper) SettleAndDistributeGVGFamily(ctx types3.Context, sp *types1.StorageProvider, family *types2.GlobalVirtualGroupFamily) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "SettleAndDistributeGVGFamily", ctx, sp, family)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// SettleAndDistributeGVGFamily indicates an expected call of SettleAndDistributeGVGFamily.
+func (mr *MockVirtualGroupKeeperMockRecorder) SettleAndDistributeGVGFamily(ctx, sp, family interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SettleAndDistributeGVGFamily", reflect.TypeOf((*MockVirtualGroupKeeper)(nil).SettleAndDistributeGVGFamily), ctx, sp, family)
+}
diff --git a/x/storage/types/genesis_test.go b/x/storage/types/genesis_test.go
index 8561ef6f3..01ed4b2be 100644
--- a/x/storage/types/genesis_test.go
+++ b/x/storage/types/genesis_test.go
@@ -29,18 +29,18 @@ func TestGenesisState_Validate(t *testing.T) {
 						RedundantParityChunkNum: 8,
 						MinChargeSize:           100,
 					},
-					MaxPayloadSize:            2000,
-					MaxBucketsPerAccount:      100,
-					MirrorBucketRelayerFee:    "10",
-					MirrorBucketAckRelayerFee: "10",
-					MirrorGroupRelayerFee:     "10",
-					MirrorGroupAckRelayerFee:  "10",
-					MirrorObjectRelayerFee:    "10",
-					MirrorObjectAckRelayerFee: "10",
-					DiscontinueCountingWindow: 1000,
-					DiscontinueObjectMax:      10000,
-					DiscontinueBucketMax:      10000,
-					DiscontinueConfirmPeriod:  100,
+					MaxPayloadSize:               2000,
+					MaxBucketsPerAccount:         100,
+					BscMirrorBucketRelayerFee:    "10",
+					BscMirrorBucketAckRelayerFee: "10",
+					BscMirrorGroupRelayerFee:     "10",
+					BscMirrorGroupAckRelayerFee:  "10",
+					BscMirrorObjectRelayerFee:    "10",
+					BscMirrorObjectAckRelayerFee: "10",
+					DiscontinueCountingWindow:    1000,
+					DiscontinueObjectMax:         10000,
+					DiscontinueBucketMax:         10000,
+					DiscontinueConfirmPeriod:     100,
 				},
 
 				// this line is used by starport scaffolding # types/genesis/validField
diff --git a/x/storage/types/keys.go b/x/storage/types/keys.go
index 05d7c6c96..de9593a43 100644
--- a/x/storage/types/keys.go
+++ b/x/storage/types/keys.go
@@ -32,10 +32,11 @@ var (
 	ParamsKey                = []byte{0x01}
 	VersionedParamsKeyPrefix = []byte{0x02}
 
-	BucketPrefix = []byte{0x11}
-	ObjectPrefix = []byte{0x12}
-	GroupPrefix  = []byte{0x13}
-	QuotaPrefix  = []byte{0x14}
+	BucketInfoPrefix         = []byte{0x11}
+	ObjectInfoPrefix         = []byte{0x12}
+	GroupInfoPrefix          = []byte{0x13}
+	QuotaPrefix              = []byte{0x14}
+	InternalBucketInfoPrefix = []byte{0x15}
 
 	BucketByIDPrefix = []byte{0x21}
 	ObjectByIDPrefix = []byte{0x22}
@@ -55,51 +56,55 @@ var (
 	//stale permission of these resources needs to be deleted.
 	// it is stored in transient store
 	CurrentBlockDeleteStalePoliciesKey = []byte{0x51}
+	DeleteStalePoliciesPrefix          = []byte{0x52}
 
-	DeleteStalePoliciesPrefix = []byte{0x52}
+	MigrateBucketPrefix = []byte{0x61}
 )
 
 // GetBucketKey return the bucket name store key
 func GetBucketKey(bucketName string) []byte {
 	objectNameHash := sdk.Keccak256([]byte(bucketName))
-	return append(BucketPrefix, objectNameHash...)
+	return append(BucketInfoPrefix, objectNameHash...)
 }
 
 // GetObjectKey return the object name store key
 func GetObjectKey(bucketName string, objectName string) []byte {
 	bucketNameHash := sdk.Keccak256([]byte(bucketName))
 	objectNameHash := sdk.Keccak256([]byte(objectName))
-	return append(ObjectPrefix, append(bucketNameHash, objectNameHash...)...)
+	return append(ObjectInfoPrefix, append(bucketNameHash, objectNameHash...)...)
 }
 
 func GetObjectKeyOnlyBucketPrefix(bucketName string) []byte {
-	return append(ObjectPrefix, sdk.Keccak256([]byte(bucketName))...)
+	return append(ObjectInfoPrefix, sdk.Keccak256([]byte(bucketName))...)
 }
 
 // GetGroupKey return the group name store key
 func GetGroupKey(owner sdk.AccAddress, groupName string) []byte {
 	groupNameHash := sdk.Keccak256([]byte(groupName))
-	return append(GroupPrefix, append(owner.Bytes(), groupNameHash...)...)
+	return append(GroupInfoPrefix, append(owner.Bytes(), groupNameHash...)...)
 }
 
 // GetGroupKeyOnlyOwnerPrefix return the group name store key
 func GetGroupKeyOnlyOwnerPrefix(owner sdk.AccAddress) []byte {
-	return append(GroupPrefix, owner.Bytes()...)
+	return append(GroupInfoPrefix, owner.Bytes()...)
 }
 
 // GetBucketByIDKey return the bucketID store key
 func GetBucketByIDKey(bucketId math.Uint) []byte {
-	return append(BucketByIDPrefix, sequence.EncodeSequence(bucketId)...)
+	var seq sequence.Sequence[math.Uint]
+	return append(BucketByIDPrefix, seq.EncodeSequence(bucketId)...)
 }
 
 // GetObjectByIDKey return the objectId store key
 func GetObjectByIDKey(objectId math.Uint) []byte {
-	return append(ObjectByIDPrefix, sequence.EncodeSequence(objectId)...)
+	var seq sequence.Sequence[math.Uint]
+	return append(ObjectByIDPrefix, seq.EncodeSequence(objectId)...)
 }
 
 // GetGroupByIDKey return the groupId store key
 func GetGroupByIDKey(groupId math.Uint) []byte {
-	return append(GroupByIDPrefix, sequence.EncodeSequence(groupId)...)
+	var seq sequence.Sequence[math.Uint]
+	return append(GroupByIDPrefix, seq.EncodeSequence(groupId)...)
 }
 
 // GetDiscontinueObjectIdsKey return discontinue object store key
@@ -118,7 +123,8 @@ func GetDiscontinueBucketIdsKey(timestamp int64) []byte {
 
 // GetDiscontinueObjectStatusKey return discontinue object status store key
 func GetDiscontinueObjectStatusKey(objectId math.Uint) []byte {
-	return append(DiscontinueObjectStatusPrefix, sequence.EncodeSequence(objectId)...)
+	var seq sequence.Sequence[math.Uint]
+	return append(DiscontinueObjectStatusPrefix, seq.EncodeSequence(objectId)...)
 }
 
 // GetParamsKeyWithTimestamp return multi-version params store key
@@ -135,7 +141,18 @@ func GetDeleteStalePoliciesKey(height int64) []byte {
 	return append(DeleteStalePoliciesPrefix, bz...)
 }
 
+func GetMigrationBucketKey(bucketID math.Uint) []byte {
+	var seq sequence.Sequence[math.Uint]
+	return append(MigrateBucketPrefix, seq.EncodeSequence(bucketID)...)
+}
+
 // GetQuotaKey return the quota store key
-func GetQuotaKey(bucketId math.Uint) []byte {
-	return append(QuotaPrefix, sequence.EncodeSequence(bucketId)...)
+func GetQuotaKey(bucketID math.Uint) []byte {
+	var seq sequence.Sequence[math.Uint]
+	return append(QuotaPrefix, seq.EncodeSequence(bucketID)...)
+}
+
+func GetInternalBucketInfoKey(bucketID math.Uint) []byte {
+	var seq sequence.Sequence[math.Uint]
+	return append(InternalBucketInfoPrefix, seq.EncodeSequence(bucketID)...)
 }
diff --git a/x/storage/types/message.go b/x/storage/types/message.go
index 39267546c..81d040ab8 100644
--- a/x/storage/types/message.go
+++ b/x/storage/types/message.go
@@ -1,6 +1,7 @@
 package types
 
 import (
+	"fmt"
 	"strings"
 	"time"
 
@@ -8,7 +9,6 @@ import (
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 	"github.com/cosmos/gogoproto/proto"
-	ethcrypto "github.com/ethereum/go-ethereum/crypto"
 
 	grn2 "github.com/bnb-chain/greenfield/types"
 	"github.com/bnb-chain/greenfield/types/common"
@@ -103,7 +103,7 @@ func NewMsgCreateBucket(
 		Visibility:        Visibility,
 		PaymentAddress:    paymentAddress.String(),
 		PrimarySpAddress:  primarySPAddress.String(),
-		PrimarySpApproval: &Approval{timeoutHeight, sig},
+		PrimarySpApproval: &common.Approval{ExpiredHeight: timeoutHeight, Sig: sig},
 		ChargedReadQuota:  chargedReadQuota,
 	}
 }
@@ -154,7 +154,7 @@ func (msg *MsgCreateBucket) ValidateBasic() error {
 	}
 
 	if msg.PrimarySpApproval == nil {
-		return errors.Wrapf(ErrInvalidApproval, "Empty approvals are not allowed.")
+		return ErrInvalidApproval.Wrap("Empty approvals are not allowed.")
 	}
 
 	// PaymentAddress is optional, use creator by default if not set.
@@ -284,25 +284,18 @@ func (msg *MsgUpdateBucketInfo) ValidateBasic() error {
 // NewMsgCreateObject creates a new MsgCreateObject instance.
 func NewMsgCreateObject(
 	creator sdk.AccAddress, bucketName string, objectName string, payloadSize uint64,
-	Visibility VisibilityType, expectChecksums [][]byte, contentType string, redundancyType RedundancyType, timeoutHeight uint64, sig []byte,
-	secondarySPAccs []sdk.AccAddress) *MsgCreateObject {
-
-	var secSPAddrs []string
-	for _, secondarySP := range secondarySPAccs {
-		secSPAddrs = append(secSPAddrs, secondarySP.String())
-	}
+	Visibility VisibilityType, expectChecksums [][]byte, contentType string, redundancyType RedundancyType, timeoutHeight uint64, sig []byte) *MsgCreateObject {
 
 	return &MsgCreateObject{
-		Creator:                    creator.String(),
-		BucketName:                 bucketName,
-		ObjectName:                 objectName,
-		PayloadSize:                payloadSize,
-		Visibility:                 Visibility,
-		ContentType:                contentType,
-		PrimarySpApproval:          &Approval{timeoutHeight, sig},
-		ExpectChecksums:            expectChecksums,
-		RedundancyType:             redundancyType,
-		ExpectSecondarySpAddresses: secSPAddrs,
+		Creator:           creator.String(),
+		BucketName:        bucketName,
+		ObjectName:        objectName,
+		PayloadSize:       payloadSize,
+		Visibility:        Visibility,
+		ContentType:       contentType,
+		PrimarySpApproval: &common.Approval{ExpiredHeight: timeoutHeight, Sig: sig},
+		ExpectChecksums:   expectChecksums,
+		RedundancyType:    redundancyType,
 	}
 }
 
@@ -362,12 +355,6 @@ func (msg *MsgCreateObject) ValidateBasic() error {
 		return err
 	}
 
-	for _, spAddress := range msg.ExpectSecondarySpAddresses {
-		if _, err = sdk.AccAddressFromHexUnsafe(spAddress); err != nil {
-			return errors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid sp address (%s) in expect secondary SPs", err)
-		}
-	}
-
 	if msg.Visibility == VISIBILITY_TYPE_UNSPECIFIED {
 		return errors.Wrapf(ErrInvalidVisibility, "Unspecified visibility is not allowed.")
 	}
@@ -481,20 +468,15 @@ func (msg *MsgDeleteObject) ValidateBasic() error {
 }
 
 func NewMsgSealObject(
-	operator sdk.AccAddress, bucketName string, objectName string,
-	secondarySPAccs []sdk.AccAddress, secondarySpSignatures [][]byte) *MsgSealObject {
-
-	var secondarySPAddresses []string
-	for _, secondarySP := range secondarySPAccs {
-		secondarySPAddresses = append(secondarySPAddresses, secondarySP.String())
-	}
+	operator sdk.AccAddress, bucketName string, objectName string, globalVirtualGroupID uint32,
+	secondarySpBlsSignatures []byte) *MsgSealObject {
 
 	return &MsgSealObject{
-		Operator:              operator.String(),
-		BucketName:            bucketName,
-		ObjectName:            objectName,
-		SecondarySpAddresses:  secondarySPAddresses,
-		SecondarySpSignatures: secondarySpSignatures,
+		Operator:                    operator.String(),
+		BucketName:                  bucketName,
+		ObjectName:                  objectName,
+		GlobalVirtualGroupId:        globalVirtualGroupID,
+		SecondarySpBlsAggSignatures: secondarySpBlsSignatures,
 	}
 }
 
@@ -540,17 +522,10 @@ func (msg *MsgSealObject) ValidateBasic() error {
 		return err
 	}
 
-	for _, addr := range msg.SecondarySpAddresses {
-		_, err := sdk.AccAddressFromHexUnsafe(addr)
-		if err != nil {
-			return errors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid secondary sp address (%s)", err)
-		}
-	}
-
-	for _, sig := range msg.SecondarySpSignatures {
-		if sig == nil && len(sig) != ethcrypto.SignatureLength {
-			return errors.Wrapf(gnfderrors.ErrInvalidSPSignature, "invalid SP signatures")
-		}
+	if len(msg.GetSecondarySpBlsAggSignatures()) != sdk.BLSSignatureLength {
+		return errors.Wrap(sdkerrors.ErrInvalidRequest,
+			fmt.Sprintf("length of signature should be %d", sdk.BLSSignatureLength),
+		)
 	}
 
 	return nil
@@ -565,7 +540,7 @@ func NewMsgCopyObject(
 		DstBucketName:        dstBucketName,
 		SrcObjectName:        srcObjectName,
 		DstObjectName:        dstObjectName,
-		DstPrimarySpApproval: &Approval{timeoutHeight, sig},
+		DstPrimarySpApproval: &common.Approval{ExpiredHeight: timeoutHeight, Sig: sig},
 	}
 }
 
@@ -1131,7 +1106,7 @@ func (msg *MsgUpdateGroupExtra) ValidateBasic() error {
 		return err
 	}
 	if len(msg.Extra) > MaxGroupExtraInfoLimit {
-		return errors.Wrapf(ErrInvalidParameter, "extra is too long with length %d", len(msg.Extra))
+		return errors.Wrapf(gnfderrors.ErrInvalidParameter, "extra is too long with length %d", len(msg.Extra))
 	}
 
 	return nil
diff --git a/x/storage/types/message_cancel_migrate_bucket.go b/x/storage/types/message_cancel_migrate_bucket.go
new file mode 100644
index 000000000..7e6214e2b
--- /dev/null
+++ b/x/storage/types/message_cancel_migrate_bucket.go
@@ -0,0 +1,53 @@
+package types
+
+import (
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+
+	"github.com/bnb-chain/greenfield/types/s3util"
+)
+
+const TypeMsgCancelMigrateBucket = "cancel_migrate_bucket"
+
+var _ sdk.Msg = &MsgCancelMigrateBucket{}
+
+func NewMsgCancelMigrateBucket(operator sdk.AccAddress, bucketName string) *MsgCancelMigrateBucket {
+	return &MsgCancelMigrateBucket{
+		Operator:   operator.String(),
+		BucketName: bucketName,
+	}
+}
+
+func (msg *MsgCancelMigrateBucket) Route() string {
+	return RouterKey
+}
+
+func (msg *MsgCancelMigrateBucket) Type() string {
+	return TypeMsgCancelMigrateBucket
+}
+
+func (msg *MsgCancelMigrateBucket) GetSigners() []sdk.AccAddress {
+	creator, err := sdk.AccAddressFromHexUnsafe(msg.Operator)
+	if err != nil {
+		panic(err)
+	}
+	return []sdk.AccAddress{creator}
+}
+
+func (msg *MsgCancelMigrateBucket) GetSignBytes() []byte {
+	bz := ModuleCdc.MustMarshalJSON(msg)
+	return sdk.MustSortJSON(bz)
+}
+
+func (msg *MsgCancelMigrateBucket) ValidateBasic() error {
+	_, err := sdk.AccAddressFromHexUnsafe(msg.Operator)
+	if err != nil {
+		return sdkerrors.ErrInvalidAddress.Wrapf("invalid creator address (%s)", err)
+	}
+
+	err = s3util.CheckValidBucketName(msg.BucketName)
+	if err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/x/storage/types/message_cancel_migrate_bucket_test.go b/x/storage/types/message_cancel_migrate_bucket_test.go
new file mode 100644
index 000000000..9e79b0995
--- /dev/null
+++ b/x/storage/types/message_cancel_migrate_bucket_test.go
@@ -0,0 +1,43 @@
+package types
+
+import (
+	"testing"
+
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	"github.com/stretchr/testify/require"
+
+	"github.com/bnb-chain/greenfield/testutil/sample"
+)
+
+func TestMsgCancelMigrateBucket_ValidateBasic(t *testing.T) {
+	tests := []struct {
+		name string
+		msg  MsgCancelMigrateBucket
+		err  error
+	}{
+		{
+			name: "invalid address",
+			msg: MsgCancelMigrateBucket{
+				Operator:   "invalid_address",
+				BucketName: testBucketName,
+			},
+			err: sdkerrors.ErrInvalidAddress,
+		}, {
+			name: "valid address",
+			msg: MsgCancelMigrateBucket{
+				Operator:   sample.AccAddress(),
+				BucketName: testBucketName,
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			err := tt.msg.ValidateBasic()
+			if tt.err != nil {
+				require.ErrorIs(t, err, tt.err)
+				return
+			}
+			require.NoError(t, err)
+		})
+	}
+}
diff --git a/x/storage/types/message_complete_migrate_bucket.go b/x/storage/types/message_complete_migrate_bucket.go
new file mode 100644
index 000000000..b3d7c648d
--- /dev/null
+++ b/x/storage/types/message_complete_migrate_bucket.go
@@ -0,0 +1,76 @@
+package types
+
+import (
+	"cosmossdk.io/errors"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+
+	gnfderrors "github.com/bnb-chain/greenfield/types/errors"
+	"github.com/bnb-chain/greenfield/types/s3util"
+	"github.com/bnb-chain/greenfield/x/virtualgroup/types"
+)
+
+const TypeMsgCompleteMigrateBucket = "complete_migrate_bucket"
+
+var _ sdk.Msg = &MsgCompleteMigrateBucket{}
+
+func NewMsgCompleteMigrateBucket(operator sdk.AccAddress, bucketName string, globalVirtualGroupFamilyID uint32, gvgMappings []*GVGMapping) *MsgCompleteMigrateBucket {
+	return &MsgCompleteMigrateBucket{
+		Operator:                   operator.String(),
+		BucketName:                 bucketName,
+		GlobalVirtualGroupFamilyId: globalVirtualGroupFamilyID,
+		GvgMappings:                gvgMappings,
+	}
+}
+
+func (msg *MsgCompleteMigrateBucket) Route() string {
+	return RouterKey
+}
+
+func (msg *MsgCompleteMigrateBucket) Type() string {
+	return TypeMsgCompleteMigrateBucket
+}
+
+func (msg *MsgCompleteMigrateBucket) GetSigners() []sdk.AccAddress {
+	creator, err := sdk.AccAddressFromHexUnsafe(msg.Operator)
+	if err != nil {
+		panic(err)
+	}
+	return []sdk.AccAddress{creator}
+}
+
+func (msg *MsgCompleteMigrateBucket) GetSignBytes() []byte {
+	bz := ModuleCdc.MustMarshalJSON(msg)
+	return sdk.MustSortJSON(bz)
+}
+
+func (msg *MsgCompleteMigrateBucket) ValidateBasic() error {
+	_, err := sdk.AccAddressFromHexUnsafe(msg.Operator)
+	if err != nil {
+		return errors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid creator address (%s)", err)
+	}
+	err = s3util.CheckValidBucketName(msg.BucketName)
+	if err != nil {
+		return err
+	}
+	if msg.GlobalVirtualGroupFamilyId == types.NoSpecifiedFamilyId {
+		return gnfderrors.ErrInvalidMessage.Wrap("the global virtual group family id not specify.")
+	}
+
+	mappingMap := make(map[uint32]uint32)
+	for _, gvgMapping := range msg.GvgMappings {
+		if gvgMapping.SrcGlobalVirtualGroupId == 0 || gvgMapping.DstGlobalVirtualGroupId == 0 {
+			return ErrInvalidGlobalVirtualGroup.Wrapf("the src gvg id cannot be 0")
+		}
+		if gvgMapping.SecondarySpBlsSignature == nil {
+			return gnfderrors.ErrInvalidBlsSignature.Wrapf("empty signature in gvgMapping")
+
+		}
+		_, exist := mappingMap[gvgMapping.SrcGlobalVirtualGroupId]
+		if exist {
+			return ErrInvalidGlobalVirtualGroup.Wrapf("src gvg id duplicates")
+		}
+		mappingMap[gvgMapping.SrcGlobalVirtualGroupId] = gvgMapping.DstGlobalVirtualGroupId
+	}
+	return nil
+}
diff --git a/x/storage/types/message_complete_migrate_bucket_test.go b/x/storage/types/message_complete_migrate_bucket_test.go
new file mode 100644
index 000000000..1ad3783d0
--- /dev/null
+++ b/x/storage/types/message_complete_migrate_bucket_test.go
@@ -0,0 +1,45 @@
+package types
+
+import (
+	"testing"
+
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	"github.com/stretchr/testify/require"
+
+	"github.com/bnb-chain/greenfield/testutil/sample"
+)
+
+func TestMsgCompleteMigrateBucket_ValidateBasic(t *testing.T) {
+	tests := []struct {
+		name string
+		msg  MsgCompleteMigrateBucket
+		err  error
+	}{
+		{
+			name: "invalid address",
+			msg: MsgCompleteMigrateBucket{
+				Operator:   "invalid_address",
+				BucketName: "bucketname",
+			},
+			err: sdkerrors.ErrInvalidAddress,
+		}, {
+			name: "valid address",
+			msg: MsgCompleteMigrateBucket{
+				Operator:                   sample.AccAddress(),
+				BucketName:                 "bucketname",
+				GlobalVirtualGroupFamilyId: 1,
+				GvgMappings:                []*GVGMapping{{1, 2, []byte("xxxxxxxxxxx")}},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			err := tt.msg.ValidateBasic()
+			if tt.err != nil {
+				require.ErrorIs(t, err, tt.err)
+				return
+			}
+			require.NoError(t, err)
+		})
+	}
+}
diff --git a/x/storage/types/message_migrate_bucket.go b/x/storage/types/message_migrate_bucket.go
new file mode 100644
index 000000000..d6998338d
--- /dev/null
+++ b/x/storage/types/message_migrate_bucket.go
@@ -0,0 +1,72 @@
+package types
+
+import (
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	"github.com/cosmos/gogoproto/proto"
+
+	"github.com/bnb-chain/greenfield/types/common"
+	"github.com/bnb-chain/greenfield/types/errors"
+	"github.com/bnb-chain/greenfield/types/s3util"
+)
+
+const TypeMsgMigrateBucket = "migrate_bucket"
+
+var _ sdk.Msg = &MsgMigrateBucket{}
+
+func NewMsgMigrateBucket(operator sdk.AccAddress, bucketName string, dstPrimarySPID uint32) *MsgMigrateBucket {
+	return &MsgMigrateBucket{
+		Operator:             operator.String(),
+		BucketName:           bucketName,
+		DstPrimarySpId:       dstPrimarySPID,
+		DstPrimarySpApproval: &common.Approval{},
+	}
+}
+
+func (msg *MsgMigrateBucket) Route() string {
+	return RouterKey
+}
+
+func (msg *MsgMigrateBucket) Type() string {
+	return TypeMsgMigrateBucket
+}
+
+func (msg *MsgMigrateBucket) GetSigners() []sdk.AccAddress {
+	creator, err := sdk.AccAddressFromHexUnsafe(msg.Operator)
+	if err != nil {
+		panic(err)
+	}
+	return []sdk.AccAddress{creator}
+}
+
+func (msg *MsgMigrateBucket) GetSignBytes() []byte {
+	bz := ModuleCdc.MustMarshalJSON(msg)
+	return sdk.MustSortJSON(bz)
+}
+
+func (msg *MsgMigrateBucket) GetApprovalBytes() []byte {
+	fakeMsg := proto.Clone(msg).(*MsgMigrateBucket)
+	fakeMsg.DstPrimarySpApproval.Sig = nil
+	return fakeMsg.GetSignBytes()
+}
+
+func (msg *MsgMigrateBucket) ValidateBasic() error {
+	_, err := sdk.AccAddressFromHexUnsafe(msg.Operator)
+	if err != nil {
+		return sdkerrors.ErrInvalidAddress.Wrapf("invalid creator address (%s)", err)
+	}
+
+	err = s3util.CheckValidBucketName(msg.BucketName)
+	if err != nil {
+		return err
+	}
+
+	if msg.DstPrimarySpId == 0 {
+		return errors.ErrInvalidMessage.Wrapf("Invalid dst primary sp id: %d", msg.DstPrimarySpId)
+	}
+
+	if msg.DstPrimarySpApproval == nil {
+		return ErrInvalidApproval.Wrap("Empty approvals are not allowed.")
+	}
+	return nil
+}
diff --git a/x/storage/types/message_migrate_bucket_test.go b/x/storage/types/message_migrate_bucket_test.go
new file mode 100644
index 000000000..84d11d107
--- /dev/null
+++ b/x/storage/types/message_migrate_bucket_test.go
@@ -0,0 +1,46 @@
+package types
+
+import (
+	"testing"
+
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	"github.com/stretchr/testify/require"
+
+	"github.com/bnb-chain/greenfield/testutil/sample"
+	"github.com/bnb-chain/greenfield/types/common"
+)
+
+func TestMsgMigrateBucket_ValidateBasic(t *testing.T) {
+	tests := []struct {
+		name string
+		msg  MsgMigrateBucket
+		err  error
+	}{
+		{
+			name: "invalid address",
+			msg: MsgMigrateBucket{
+				Operator:   "invalid_address",
+				BucketName: "bucketname",
+			},
+			err: sdkerrors.ErrInvalidAddress,
+		}, {
+			name: "valid address",
+			msg: MsgMigrateBucket{
+				Operator:             sample.AccAddress(),
+				BucketName:           "bucketname",
+				DstPrimarySpId:       1,
+				DstPrimarySpApproval: &common.Approval{ExpiredHeight: 10, Sig: []byte("XXXTentacion")},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			err := tt.msg.ValidateBasic()
+			if tt.err != nil {
+				require.ErrorIs(t, err, tt.err)
+				return
+			}
+			require.NoError(t, err)
+		})
+	}
+}
diff --git a/x/storage/types/message_test.go b/x/storage/types/message_test.go
index 80530bc0c..bb331524e 100644
--- a/x/storage/types/message_test.go
+++ b/x/storage/types/message_test.go
@@ -7,6 +7,7 @@ import (
 	"cosmossdk.io/math"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	"github.com/prysmaticlabs/prysm/crypto/bls"
 	"github.com/stretchr/testify/require"
 
 	"github.com/bnb-chain/greenfield/testutil/sample"
@@ -37,7 +38,7 @@ func TestMsgCreateBucket_ValidateBasic(t *testing.T) {
 				Visibility:        VISIBILITY_TYPE_PUBLIC_READ,
 				PaymentAddress:    sample.AccAddress(),
 				PrimarySpAddress:  sample.AccAddress(),
-				PrimarySpApproval: &Approval{},
+				PrimarySpApproval: &common.Approval{},
 			},
 		}, {
 			name: "invalid bucket name",
@@ -47,7 +48,7 @@ func TestMsgCreateBucket_ValidateBasic(t *testing.T) {
 				Visibility:        VISIBILITY_TYPE_PUBLIC_READ,
 				PaymentAddress:    sample.AccAddress(),
 				PrimarySpAddress:  sample.AccAddress(),
-				PrimarySpApproval: &Approval{},
+				PrimarySpApproval: &common.Approval{},
 			},
 			err: gnfderrors.ErrInvalidBucketName,
 		}, {
@@ -58,7 +59,7 @@ func TestMsgCreateBucket_ValidateBasic(t *testing.T) {
 				Visibility:        VISIBILITY_TYPE_PUBLIC_READ,
 				PaymentAddress:    sample.AccAddress(),
 				PrimarySpAddress:  sample.AccAddress(),
-				PrimarySpApproval: &Approval{},
+				PrimarySpApproval: &common.Approval{},
 			},
 			err: gnfderrors.ErrInvalidBucketName,
 		}, {
@@ -69,7 +70,7 @@ func TestMsgCreateBucket_ValidateBasic(t *testing.T) {
 				Visibility:        VISIBILITY_TYPE_PUBLIC_READ,
 				PaymentAddress:    sample.AccAddress(),
 				PrimarySpAddress:  sample.AccAddress(),
-				PrimarySpApproval: &Approval{},
+				PrimarySpApproval: &common.Approval{},
 			},
 			err: gnfderrors.ErrInvalidBucketName,
 		}, {
@@ -80,7 +81,7 @@ func TestMsgCreateBucket_ValidateBasic(t *testing.T) {
 				Visibility:        VISIBILITY_TYPE_PUBLIC_READ,
 				PaymentAddress:    sample.AccAddress(),
 				PrimarySpAddress:  sample.AccAddress(),
-				PrimarySpApproval: &Approval{},
+				PrimarySpApproval: &common.Approval{},
 			},
 			err: gnfderrors.ErrInvalidBucketName,
 		},
@@ -167,56 +168,52 @@ func TestMsgCreateObject_ValidateBasic(t *testing.T) {
 		{
 			name: "normal",
 			msg: MsgCreateObject{
-				Creator:                    sample.AccAddress(),
-				BucketName:                 testBucketName,
-				ObjectName:                 testObjectName,
-				PayloadSize:                1024,
-				Visibility:                 VISIBILITY_TYPE_PRIVATE,
-				ContentType:                "content-type",
-				PrimarySpApproval:          &Approval{},
-				ExpectChecksums:            [][]byte{sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum()},
-				ExpectSecondarySpAddresses: []string{sample.AccAddress(), sample.AccAddress(), sample.AccAddress(), sample.AccAddress(), sample.AccAddress(), sample.AccAddress()},
+				Creator:           sample.AccAddress(),
+				BucketName:        testBucketName,
+				ObjectName:        testObjectName,
+				PayloadSize:       1024,
+				Visibility:        VISIBILITY_TYPE_PRIVATE,
+				ContentType:       "content-type",
+				PrimarySpApproval: &common.Approval{},
+				ExpectChecksums:   [][]byte{sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum()},
 			},
 		}, {
 			name: "invalid object name",
 			msg: MsgCreateObject{
-				Creator:                    sample.AccAddress(),
-				BucketName:                 testBucketName,
-				ObjectName:                 "",
-				PayloadSize:                1024,
-				Visibility:                 VISIBILITY_TYPE_PRIVATE,
-				ContentType:                "content-type",
-				PrimarySpApproval:          &Approval{},
-				ExpectChecksums:            [][]byte{sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum()},
-				ExpectSecondarySpAddresses: []string{sample.AccAddress(), sample.AccAddress(), sample.AccAddress(), sample.AccAddress(), sample.AccAddress(), sample.AccAddress()},
+				Creator:           sample.AccAddress(),
+				BucketName:        testBucketName,
+				ObjectName:        "",
+				PayloadSize:       1024,
+				Visibility:        VISIBILITY_TYPE_PRIVATE,
+				ContentType:       "content-type",
+				PrimarySpApproval: &common.Approval{},
+				ExpectChecksums:   [][]byte{sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum()},
 			},
 			err: gnfderrors.ErrInvalidObjectName,
 		}, {
 			name: "invalid object name",
 			msg: MsgCreateObject{
-				Creator:                    sample.AccAddress(),
-				BucketName:                 testBucketName,
-				ObjectName:                 "../object",
-				PayloadSize:                1024,
-				Visibility:                 VISIBILITY_TYPE_PRIVATE,
-				ContentType:                "content-type",
-				PrimarySpApproval:          &Approval{},
-				ExpectChecksums:            [][]byte{sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum()},
-				ExpectSecondarySpAddresses: []string{sample.AccAddress(), sample.AccAddress(), sample.AccAddress(), sample.AccAddress(), sample.AccAddress(), sample.AccAddress()},
+				Creator:           sample.AccAddress(),
+				BucketName:        testBucketName,
+				ObjectName:        "../object",
+				PayloadSize:       1024,
+				Visibility:        VISIBILITY_TYPE_PRIVATE,
+				ContentType:       "content-type",
+				PrimarySpApproval: &common.Approval{},
+				ExpectChecksums:   [][]byte{sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum()},
 			},
 			err: gnfderrors.ErrInvalidObjectName,
 		}, {
 			name: "invalid object name",
 			msg: MsgCreateObject{
-				Creator:                    sample.AccAddress(),
-				BucketName:                 testBucketName,
-				ObjectName:                 "//object",
-				PayloadSize:                1024,
-				Visibility:                 VISIBILITY_TYPE_PRIVATE,
-				ContentType:                "content-type",
-				PrimarySpApproval:          &Approval{},
-				ExpectChecksums:            [][]byte{sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum()},
-				ExpectSecondarySpAddresses: []string{sample.AccAddress(), sample.AccAddress(), sample.AccAddress(), sample.AccAddress(), sample.AccAddress(), sample.AccAddress()},
+				Creator:           sample.AccAddress(),
+				BucketName:        testBucketName,
+				ObjectName:        "//object",
+				PayloadSize:       1024,
+				Visibility:        VISIBILITY_TYPE_PRIVATE,
+				ContentType:       "content-type",
+				PrimarySpApproval: &common.Approval{},
+				ExpectChecksums:   [][]byte{sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum()},
 			},
 			err: gnfderrors.ErrInvalidObjectName,
 		},
@@ -301,7 +298,7 @@ func TestMsgCopyObject_ValidateBasic(t *testing.T) {
 				SrcObjectName: testObjectName,
 				DstBucketName: "dst" + testBucketName,
 				DstObjectName: "dst" + testObjectName,
-				DstPrimarySpApproval: &Approval{
+				DstPrimarySpApproval: &common.Approval{
 					ExpiredHeight: 100,
 					Sig:           []byte("xxx"),
 				},
@@ -321,6 +318,10 @@ func TestMsgCopyObject_ValidateBasic(t *testing.T) {
 }
 
 func TestMsgSealObject_ValidateBasic(t *testing.T) {
+	checksums := [][]byte{sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum()}
+	blsSignDoc := NewSecondarySpSealObjectSignDoc("greenfield_9000-1", 1, math.NewUint(1), GenerateHash(checksums[:])).GetSignBytes()
+	blsPrivKey, _ := bls.RandKey()
+	aggSig := blsPrivKey.Sign(blsSignDoc[:]).Marshal()
 	tests := []struct {
 		name string
 		msg  MsgSealObject
@@ -329,11 +330,10 @@ func TestMsgSealObject_ValidateBasic(t *testing.T) {
 		{
 			name: "normal",
 			msg: MsgSealObject{
-				Operator:              sample.AccAddress(),
-				BucketName:            testBucketName,
-				ObjectName:            testObjectName,
-				SecondarySpAddresses:  []string{sample.AccAddress(), sample.AccAddress(), sample.AccAddress(), sample.AccAddress(), sample.AccAddress(), sample.AccAddress()},
-				SecondarySpSignatures: [][]byte{sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum(), sample.Checksum()},
+				Operator:                    sample.AccAddress(),
+				BucketName:                  testBucketName,
+				ObjectName:                  testObjectName,
+				SecondarySpBlsAggSignatures: aggSig,
 			},
 		},
 	}
@@ -547,7 +547,7 @@ func TestMsgUpdateGroupExtra_ValidateBasic(t *testing.T) {
 				GroupName:  testGroupName,
 				Extra:      strings.Repeat("abcdefg", 80),
 			},
-			err: ErrInvalidParameter,
+			err: gnfderrors.ErrInvalidParameter,
 		},
 	}
 	for _, tt := range tests {
diff --git a/x/storage/types/params.go b/x/storage/types/params.go
index e62d84071..6c2b44bc3 100644
--- a/x/storage/types/params.go
+++ b/x/storage/types/params.go
@@ -14,7 +14,7 @@ const (
 	DefaultMaxSegmentSize            uint64 = 16 * 1024 * 1024 // 16M
 	DefaultRedundantDataChunkNum     uint32 = 4
 	DefaultRedundantParityChunkNum   uint32 = 2
-	DefaultMaxPayloadSize            uint64 = 2 * 1024 * 1024 * 1024
+	DefaultMaxPayloadSize            uint64 = 64 * 1024 * 1024 * 1024
 	DefaultMaxBucketsPerAccount      uint32 = 100
 	DefaultMinChargeSize             uint64 = 1 * 1024 * 1024 // 1M
 	DefaultDiscontinueCountingWindow uint64 = 10000
@@ -25,34 +25,36 @@ const (
 	DefaultStalePolicyCleanupMax     uint64 = 200
 	DefaultMinUpdateQuotaInterval    uint64 = 2592000 // 30 days (in second)
 
-	DefaultMirrorBucketRelayerFee    = "250000000000000" // 0.00025
-	DefaultMirrorBucketAckRelayerFee = "250000000000000" // 0.00025
-	DefaultMirrorObjectRelayerFee    = "250000000000000" // 0.00025
-	DefaultMirrorObjectAckRelayerFee = "250000000000000" // 0.00025
-	DefaultMirrorGroupRelayerFee     = "250000000000000" // 0.00025
-	DefaultMirrorGroupAckRelayerFee  = "250000000000000" // 0.00025
+	DefaultMaxLocalVirtualGroupNumPerBucket uint32 = 10
+	DefaultMirrorBucketRelayerFee                  = "250000000000000" // 0.00025
+	DefaultMirrorBucketAckRelayerFee               = "250000000000000" // 0.00025
+	DefaultMirrorObjectRelayerFee                  = "250000000000000" // 0.00025
+	DefaultMirrorObjectAckRelayerFee               = "250000000000000" // 0.00025
+	DefaultMirrorGroupRelayerFee                   = "250000000000000" // 0.00025
+	DefaultMirrorGroupAckRelayerFee                = "250000000000000" // 0.00025
 )
 
 var (
-	KeyMaxSegmentSize            = []byte("MaxSegmentSize")
-	KeyRedundantDataChunkNum     = []byte("RedundantDataChunkNum")
-	KeyRedundantParityChunkNum   = []byte("RedundantParityChunkNum")
-	KeyMaxPayloadSize            = []byte("MaxPayloadSize")
-	KeyMinChargeSize             = []byte("MinChargeSize")
-	KeyMaxBucketsPerAccount      = []byte("MaxBucketsPerAccount")
-	KeyDiscontinueCountingWindow = []byte("DiscontinueCountingWindow")
-	KeyDiscontinueObjectMax      = []byte("DiscontinueObjectMax")
-	KeyDiscontinueBucketMax      = []byte("DiscontinueBucketMax")
-	KeyDiscontinueConfirmPeriod  = []byte("DiscontinueConfirmPeriod")
-	KeyDiscontinueDeletionMax    = []byte("DiscontinueDeletionMax")
-	KeyStalePolicyCleanupMax     = []byte("StalePolicyCleanupMax")
-	KeyMinUpdateQuotaInterval    = []byte("MinUpdateQuotaInterval")
-	KeyMirrorBucketRelayerFee    = []byte("MirrorBucketRelayerFee")
-	KeyMirrorBucketAckRelayerFee = []byte("MirrorBucketAckRelayerFee")
-	KeyMirrorObjectRelayerFee    = []byte("MirrorObjectRelayerFee")
-	KeyMirrorObjectAckRelayerFee = []byte("MirrorObjectAckRelayerFee")
-	KeyMirrorGroupRelayerFee     = []byte("MirrorGroupRelayerFee")
-	KeyMirrorGroupAckRelayerFee  = []byte("MirrorGroupAckRelayerFee")
+	KeyMaxSegmentSize                   = []byte("MaxSegmentSize")
+	KeyRedundantDataChunkNum            = []byte("RedundantDataChunkNum")
+	KeyRedundantParityChunkNum          = []byte("RedundantParityChunkNum")
+	KeyMaxPayloadSize                   = []byte("MaxPayloadSize")
+	KeyMinChargeSize                    = []byte("MinChargeSize")
+	KeyMaxBucketsPerAccount             = []byte("MaxBucketsPerAccount")
+	KeyDiscontinueCountingWindow        = []byte("DiscontinueCountingWindow")
+	KeyDiscontinueObjectMax             = []byte("DiscontinueObjectMax")
+	KeyDiscontinueBucketMax             = []byte("DiscontinueBucketMax")
+	KeyDiscontinueConfirmPeriod         = []byte("DiscontinueConfirmPeriod")
+	KeyDiscontinueDeletionMax           = []byte("DiscontinueDeletionMax")
+	KeyStalePolicyCleanupMax            = []byte("StalePolicyCleanupMax")
+	KeyMinUpdateQuotaInterval           = []byte("MinUpdateQuotaInterval")
+	KeyBscMirrorBucketRelayerFee        = []byte("BscMirrorBucketRelayerFee")
+	KeyBscMirrorBucketAckRelayerFee     = []byte("BscMirrorBucketAckRelayerFee")
+	KeyBscMirrorObjectRelayerFee        = []byte("BscMirrorObjectRelayerFee")
+	KeyBscMirrorObjectAckRelayerFee     = []byte("BscMirrorObjectAckRelayerFee")
+	KeyBscMirrorGroupRelayerFee         = []byte("BscMirrorGroupRelayerFee")
+	KeyBscMirrorGroupAckRelayerFee      = []byte("BscMirrorGroupAckRelayerFee")
+	KeyMaxLocalVirtualGroupNumPerBucket = []byte("MaxLocalVirtualGroupNumPerBucket")
 )
 
 var _ paramtypes.ParamSet = (*Params)(nil)
@@ -74,6 +76,7 @@ func NewParams(
 	discontinueDeletionMax uint64,
 	stalePoliesCleanupMax uint64,
 	minUpdateQuotaInterval uint64,
+	maxLocalVirtualGroupNumPerBucket uint32,
 ) Params {
 	return Params{
 		VersionedParams: VersionedParams{
@@ -82,21 +85,22 @@ func NewParams(
 			RedundantParityChunkNum: redundantParityChunkNum,
 			MinChargeSize:           minChargeSize,
 		},
-		MaxPayloadSize:            maxPayloadSize,
-		MaxBucketsPerAccount:      maxBucketsPerAccount,
-		MirrorBucketRelayerFee:    mirrorBucketRelayerFee,
-		MirrorBucketAckRelayerFee: mirrorBucketAckRelayerFee,
-		MirrorObjectRelayerFee:    mirrorObjectRelayerFee,
-		MirrorObjectAckRelayerFee: mirrorObjectAckRelayerFee,
-		MirrorGroupRelayerFee:     mirrorGroupRelayerFee,
-		MirrorGroupAckRelayerFee:  mirrorGroupAckRelayerFee,
-		DiscontinueCountingWindow: discontinueCountingWindow,
-		DiscontinueObjectMax:      discontinueObjectMax,
-		DiscontinueBucketMax:      discontinueBucketMax,
-		DiscontinueConfirmPeriod:  discontinueConfirmPeriod,
-		DiscontinueDeletionMax:    discontinueDeletionMax,
-		StalePolicyCleanupMax:     stalePoliesCleanupMax,
-		MinQuotaUpdateInterval:    minUpdateQuotaInterval,
+		MaxPayloadSize:                   maxPayloadSize,
+		MaxBucketsPerAccount:             maxBucketsPerAccount,
+		BscMirrorBucketRelayerFee:        mirrorBucketRelayerFee,
+		BscMirrorBucketAckRelayerFee:     mirrorBucketAckRelayerFee,
+		BscMirrorObjectRelayerFee:        mirrorObjectRelayerFee,
+		BscMirrorObjectAckRelayerFee:     mirrorObjectAckRelayerFee,
+		BscMirrorGroupRelayerFee:         mirrorGroupRelayerFee,
+		BscMirrorGroupAckRelayerFee:      mirrorGroupAckRelayerFee,
+		DiscontinueCountingWindow:        discontinueCountingWindow,
+		DiscontinueObjectMax:             discontinueObjectMax,
+		DiscontinueBucketMax:             discontinueBucketMax,
+		DiscontinueConfirmPeriod:         discontinueConfirmPeriod,
+		DiscontinueDeletionMax:           discontinueDeletionMax,
+		StalePolicyCleanupMax:            stalePoliesCleanupMax,
+		MinQuotaUpdateInterval:           minUpdateQuotaInterval,
+		MaxLocalVirtualGroupNumPerBucket: maxLocalVirtualGroupNumPerBucket,
 	}
 }
 
@@ -110,7 +114,7 @@ func DefaultParams() Params {
 		DefaultMirrorGroupRelayerFee, DefaultMirrorGroupAckRelayerFee,
 		DefaultDiscontinueCountingWindow, DefaultDiscontinueObjectMax, DefaultDiscontinueBucketMax,
 		DefaultDiscontinueConfirmPeriod, DefaultDiscontinueDeletionMax, DefaultStalePolicyCleanupMax,
-		DefaultMinUpdateQuotaInterval,
+		DefaultMinUpdateQuotaInterval, DefaultMaxLocalVirtualGroupNumPerBucket,
 	)
 }
 
@@ -124,12 +128,12 @@ func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs {
 
 		paramtypes.NewParamSetPair(KeyMaxPayloadSize, &p.MaxPayloadSize, validateMaxPayloadSize),
 		paramtypes.NewParamSetPair(KeyMaxBucketsPerAccount, &p.MaxBucketsPerAccount, validateMaxBucketsPerAccount),
-		paramtypes.NewParamSetPair(KeyMirrorBucketRelayerFee, &p.MirrorBucketRelayerFee, validateRelayerFee),
-		paramtypes.NewParamSetPair(KeyMirrorBucketAckRelayerFee, &p.MirrorBucketAckRelayerFee, validateRelayerFee),
-		paramtypes.NewParamSetPair(KeyMirrorObjectRelayerFee, &p.MirrorObjectRelayerFee, validateRelayerFee),
-		paramtypes.NewParamSetPair(KeyMirrorObjectAckRelayerFee, &p.MirrorObjectAckRelayerFee, validateRelayerFee),
-		paramtypes.NewParamSetPair(KeyMirrorGroupRelayerFee, &p.MirrorGroupRelayerFee, validateRelayerFee),
-		paramtypes.NewParamSetPair(KeyMirrorGroupAckRelayerFee, &p.MirrorGroupAckRelayerFee, validateRelayerFee),
+		paramtypes.NewParamSetPair(KeyBscMirrorBucketRelayerFee, &p.BscMirrorBucketRelayerFee, validateRelayerFee),
+		paramtypes.NewParamSetPair(KeyBscMirrorBucketAckRelayerFee, &p.BscMirrorBucketAckRelayerFee, validateRelayerFee),
+		paramtypes.NewParamSetPair(KeyBscMirrorObjectRelayerFee, &p.BscMirrorObjectRelayerFee, validateRelayerFee),
+		paramtypes.NewParamSetPair(KeyBscMirrorObjectAckRelayerFee, &p.BscMirrorObjectAckRelayerFee, validateRelayerFee),
+		paramtypes.NewParamSetPair(KeyBscMirrorGroupRelayerFee, &p.BscMirrorGroupRelayerFee, validateRelayerFee),
+		paramtypes.NewParamSetPair(KeyBscMirrorGroupAckRelayerFee, &p.BscMirrorGroupAckRelayerFee, validateRelayerFee),
 		paramtypes.NewParamSetPair(KeyDiscontinueCountingWindow, &p.DiscontinueCountingWindow, validateDiscontinueCountingWindow),
 		paramtypes.NewParamSetPair(KeyDiscontinueObjectMax, &p.DiscontinueObjectMax, validateDiscontinueObjectMax),
 		paramtypes.NewParamSetPair(KeyDiscontinueBucketMax, &p.DiscontinueBucketMax, validateDiscontinueBucketMax),
@@ -137,6 +141,7 @@ func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs {
 		paramtypes.NewParamSetPair(KeyDiscontinueDeletionMax, &p.DiscontinueDeletionMax, validateDiscontinueDeletionMax),
 		paramtypes.NewParamSetPair(KeyStalePolicyCleanupMax, &p.StalePolicyCleanupMax, validateStalePolicyCleanupMax),
 		paramtypes.NewParamSetPair(KeyMinUpdateQuotaInterval, &p.MinQuotaUpdateInterval, validateMinUpdateQuotaInterval),
+		paramtypes.NewParamSetPair(KeyMaxLocalVirtualGroupNumPerBucket, &p.MaxLocalVirtualGroupNumPerBucket, validateMaxLocalVirtualGroupNumPerBucket),
 	}
 }
 
@@ -154,29 +159,28 @@ func (p Params) Validate() error {
 	if err := validateMinChargeSize(p.VersionedParams.MinChargeSize); err != nil {
 		return err
 	}
-
 	if err := validateMaxPayloadSize(p.MaxPayloadSize); err != nil {
 		return err
 	}
 	if err := validateMaxBucketsPerAccount(p.MaxBucketsPerAccount); err != nil {
 		return err
 	}
-	if err := validateRelayerFee(p.MirrorBucketRelayerFee); err != nil {
+	if err := validateRelayerFee(p.BscMirrorBucketRelayerFee); err != nil {
 		return err
 	}
-	if err := validateRelayerFee(p.MirrorBucketAckRelayerFee); err != nil {
+	if err := validateRelayerFee(p.BscMirrorBucketAckRelayerFee); err != nil {
 		return err
 	}
-	if err := validateRelayerFee(p.MirrorObjectRelayerFee); err != nil {
+	if err := validateRelayerFee(p.BscMirrorObjectRelayerFee); err != nil {
 		return err
 	}
-	if err := validateRelayerFee(p.MirrorObjectAckRelayerFee); err != nil {
+	if err := validateRelayerFee(p.BscMirrorObjectAckRelayerFee); err != nil {
 		return err
 	}
-	if err := validateRelayerFee(p.MirrorGroupRelayerFee); err != nil {
+	if err := validateRelayerFee(p.BscMirrorGroupRelayerFee); err != nil {
 		return err
 	}
-	if err := validateRelayerFee(p.MirrorGroupAckRelayerFee); err != nil {
+	if err := validateRelayerFee(p.BscMirrorGroupAckRelayerFee); err != nil {
 		return err
 	}
 	if err := validateDiscontinueCountingWindow(p.DiscontinueCountingWindow); err != nil {
@@ -406,3 +410,16 @@ func validateMinUpdateQuotaInterval(i interface{}) error {
 
 	return nil
 }
+
+func validateMaxLocalVirtualGroupNumPerBucket(i interface{}) error {
+	v, ok := i.(uint32)
+	if !ok {
+		return fmt.Errorf("invalid parameter type: %T", i)
+	}
+
+	if v == 0 {
+		return fmt.Errorf("max buckets per account must be positive: %d", v)
+	}
+
+	return nil
+}
diff --git a/x/storage/types/params.pb.go b/x/storage/types/params.pb.go
index 1a3fc0d9d..448648a29 100644
--- a/x/storage/types/params.pb.go
+++ b/x/storage/types/params.pb.go
@@ -28,18 +28,18 @@ type Params struct {
 	VersionedParams VersionedParams `protobuf:"bytes,1,opt,name=versioned_params,json=versionedParams,proto3" json:"versioned_params"`
 	// max_payload_size is the maximum size of the payload, default: 2G
 	MaxPayloadSize uint64 `protobuf:"varint,2,opt,name=max_payload_size,json=maxPayloadSize,proto3" json:"max_payload_size,omitempty"`
-	// relayer fee for the mirror bucket tx
-	MirrorBucketRelayerFee string `protobuf:"bytes,3,opt,name=mirror_bucket_relayer_fee,json=mirrorBucketRelayerFee,proto3" json:"mirror_bucket_relayer_fee,omitempty"`
-	// relayer fee for the ACK or FAIL_ACK package of the mirror bucket tx
-	MirrorBucketAckRelayerFee string `protobuf:"bytes,4,opt,name=mirror_bucket_ack_relayer_fee,json=mirrorBucketAckRelayerFee,proto3" json:"mirror_bucket_ack_relayer_fee,omitempty"`
-	// relayer fee for the mirror object tx
-	MirrorObjectRelayerFee string `protobuf:"bytes,5,opt,name=mirror_object_relayer_fee,json=mirrorObjectRelayerFee,proto3" json:"mirror_object_relayer_fee,omitempty"`
-	// Relayer fee for the ACK or FAIL_ACK package of the mirror object tx
-	MirrorObjectAckRelayerFee string `protobuf:"bytes,6,opt,name=mirror_object_ack_relayer_fee,json=mirrorObjectAckRelayerFee,proto3" json:"mirror_object_ack_relayer_fee,omitempty"`
-	// relayer fee for the mirror object tx
-	MirrorGroupRelayerFee string `protobuf:"bytes,7,opt,name=mirror_group_relayer_fee,json=mirrorGroupRelayerFee,proto3" json:"mirror_group_relayer_fee,omitempty"`
-	// Relayer fee for the ACK or FAIL_ACK package of the mirror object tx
-	MirrorGroupAckRelayerFee string `protobuf:"bytes,8,opt,name=mirror_group_ack_relayer_fee,json=mirrorGroupAckRelayerFee,proto3" json:"mirror_group_ack_relayer_fee,omitempty"`
+	// relayer fee for the mirror bucket tx to bsc
+	BscMirrorBucketRelayerFee string `protobuf:"bytes,3,opt,name=bsc_mirror_bucket_relayer_fee,json=bscMirrorBucketRelayerFee,proto3" json:"bsc_mirror_bucket_relayer_fee,omitempty"`
+	// relayer fee for the ACK or FAIL_ACK package of the mirror bucket tx to bsc
+	BscMirrorBucketAckRelayerFee string `protobuf:"bytes,4,opt,name=bsc_mirror_bucket_ack_relayer_fee,json=bscMirrorBucketAckRelayerFee,proto3" json:"bsc_mirror_bucket_ack_relayer_fee,omitempty"`
+	// relayer fee for the mirror object tx to bsc
+	BscMirrorObjectRelayerFee string `protobuf:"bytes,5,opt,name=bsc_mirror_object_relayer_fee,json=bscMirrorObjectRelayerFee,proto3" json:"bsc_mirror_object_relayer_fee,omitempty"`
+	// Relayer fee for the ACK or FAIL_ACK package of the mirror object tx to bsc
+	BscMirrorObjectAckRelayerFee string `protobuf:"bytes,6,opt,name=bsc_mirror_object_ack_relayer_fee,json=bscMirrorObjectAckRelayerFee,proto3" json:"bsc_mirror_object_ack_relayer_fee,omitempty"`
+	// relayer fee for the mirror object tx to bsc
+	BscMirrorGroupRelayerFee string `protobuf:"bytes,7,opt,name=bsc_mirror_group_relayer_fee,json=bscMirrorGroupRelayerFee,proto3" json:"bsc_mirror_group_relayer_fee,omitempty"`
+	// Relayer fee for the ACK or FAIL_ACK package of the mirror object tx to bsc
+	BscMirrorGroupAckRelayerFee string `protobuf:"bytes,8,opt,name=bsc_mirror_group_ack_relayer_fee,json=bscMirrorGroupAckRelayerFee,proto3" json:"bsc_mirror_group_ack_relayer_fee,omitempty"`
 	// The maximum number of buckets that can be created per account
 	MaxBucketsPerAccount uint32 `protobuf:"varint,9,opt,name=max_buckets_per_account,json=maxBucketsPerAccount,proto3" json:"max_buckets_per_account,omitempty"`
 	// The window to count the discontinued objects or buckets
@@ -56,6 +56,8 @@ type Params struct {
 	StalePolicyCleanupMax uint64 `protobuf:"varint,15,opt,name=stale_policy_cleanup_max,json=stalePolicyCleanupMax,proto3" json:"stale_policy_cleanup_max,omitempty"`
 	// The min interval for making quota smaller in seconds
 	MinQuotaUpdateInterval uint64 `protobuf:"varint,16,opt,name=min_quota_update_interval,json=minQuotaUpdateInterval,proto3" json:"min_quota_update_interval,omitempty"`
+	// the max number of local virtual group per bucket
+	MaxLocalVirtualGroupNumPerBucket uint32 `protobuf:"varint,17,opt,name=max_local_virtual_group_num_per_bucket,json=maxLocalVirtualGroupNumPerBucket,proto3" json:"max_local_virtual_group_num_per_bucket,omitempty"`
 }
 
 func (m *Params) Reset()      { *m = Params{} }
@@ -104,44 +106,44 @@ func (m *Params) GetMaxPayloadSize() uint64 {
 	return 0
 }
 
-func (m *Params) GetMirrorBucketRelayerFee() string {
+func (m *Params) GetBscMirrorBucketRelayerFee() string {
 	if m != nil {
-		return m.MirrorBucketRelayerFee
+		return m.BscMirrorBucketRelayerFee
 	}
 	return ""
 }
 
-func (m *Params) GetMirrorBucketAckRelayerFee() string {
+func (m *Params) GetBscMirrorBucketAckRelayerFee() string {
 	if m != nil {
-		return m.MirrorBucketAckRelayerFee
+		return m.BscMirrorBucketAckRelayerFee
 	}
 	return ""
 }
 
-func (m *Params) GetMirrorObjectRelayerFee() string {
+func (m *Params) GetBscMirrorObjectRelayerFee() string {
 	if m != nil {
-		return m.MirrorObjectRelayerFee
+		return m.BscMirrorObjectRelayerFee
 	}
 	return ""
 }
 
-func (m *Params) GetMirrorObjectAckRelayerFee() string {
+func (m *Params) GetBscMirrorObjectAckRelayerFee() string {
 	if m != nil {
-		return m.MirrorObjectAckRelayerFee
+		return m.BscMirrorObjectAckRelayerFee
 	}
 	return ""
 }
 
-func (m *Params) GetMirrorGroupRelayerFee() string {
+func (m *Params) GetBscMirrorGroupRelayerFee() string {
 	if m != nil {
-		return m.MirrorGroupRelayerFee
+		return m.BscMirrorGroupRelayerFee
 	}
 	return ""
 }
 
-func (m *Params) GetMirrorGroupAckRelayerFee() string {
+func (m *Params) GetBscMirrorGroupAckRelayerFee() string {
 	if m != nil {
-		return m.MirrorGroupAckRelayerFee
+		return m.BscMirrorGroupAckRelayerFee
 	}
 	return ""
 }
@@ -202,6 +204,13 @@ func (m *Params) GetMinQuotaUpdateInterval() uint64 {
 	return 0
 }
 
+func (m *Params) GetMaxLocalVirtualGroupNumPerBucket() uint32 {
+	if m != nil {
+		return m.MaxLocalVirtualGroupNumPerBucket
+	}
+	return 0
+}
+
 // VersionedParams defines the parameters for the storage module with multi version, each version store with different timestamp.
 type VersionedParams struct {
 	// max_segment_size is the maximum size of a segment. default: 16M
@@ -282,50 +291,54 @@ func init() {
 func init() { proto.RegisterFile("greenfield/storage/params.proto", fileDescriptor_127b8b1511d84eca) }
 
 var fileDescriptor_127b8b1511d84eca = []byte{
-	// 687 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x94, 0x4d, 0x4f, 0xdb, 0x4a,
-	0x14, 0x86, 0xe3, 0x4b, 0x2e, 0x5c, 0x86, 0x1b, 0x82, 0x2c, 0x3e, 0x0c, 0xf7, 0x36, 0x44, 0x54,
-	0xaa, 0xb2, 0x69, 0x22, 0xf5, 0x43, 0xf4, 0x4b, 0xa8, 0x10, 0xd4, 0x8a, 0x45, 0xdb, 0xd4, 0xb4,
-	0x54, 0xea, 0x66, 0x34, 0xb1, 0x0f, 0xce, 0x34, 0xf6, 0x8c, 0x3b, 0x1e, 0x43, 0xc2, 0xaf, 0xe8,
-	0xb2, 0xcb, 0xfe, 0x1c, 0x96, 0x2c, 0xbb, 0x6a, 0x2b, 0xf8, 0x23, 0x95, 0xcf, 0xb8, 0xc1, 0x06,
-	0x76, 0xd6, 0x79, 0xdf, 0x67, 0xde, 0x33, 0x33, 0x3e, 0x43, 0xd6, 0x03, 0x05, 0x20, 0x0e, 0x39,
-	0x84, 0x7e, 0x27, 0xd1, 0x52, 0xb1, 0x00, 0x3a, 0x31, 0x53, 0x2c, 0x4a, 0xda, 0xb1, 0x92, 0x5a,
-	0xda, 0xf6, 0xa5, 0xa1, 0x9d, 0x1b, 0xd6, 0x16, 0x03, 0x19, 0x48, 0x94, 0x3b, 0xd9, 0x97, 0x71,
-	0x6e, 0x9c, 0xcd, 0x90, 0xe9, 0x1e, 0xa2, 0xf6, 0x3b, 0xb2, 0x70, 0x04, 0x2a, 0xe1, 0x52, 0x80,
-	0x4f, 0xcd, 0x72, 0x8e, 0xd5, 0xb4, 0x5a, 0x73, 0xf7, 0x6e, 0xb7, 0xaf, 0xaf, 0xd7, 0x3e, 0xf8,
-	0xe3, 0x35, 0xf8, 0x4e, 0xf5, 0xf4, 0xc7, 0x7a, 0xc5, 0xad, 0x1f, 0x95, 0xcb, 0x76, 0x8b, 0x2c,
-	0x44, 0x6c, 0x44, 0x63, 0x36, 0x0e, 0x25, 0xf3, 0x69, 0xc2, 0x4f, 0xc0, 0xf9, 0xab, 0x69, 0xb5,
-	0xaa, 0xee, 0x7c, 0xc4, 0x46, 0x3d, 0x53, 0xde, 0xe7, 0x27, 0x60, 0x3f, 0x26, 0xab, 0x11, 0x57,
-	0x4a, 0x2a, 0xda, 0x4f, 0xbd, 0x21, 0x68, 0xaa, 0x20, 0x64, 0x63, 0x50, 0xf4, 0x10, 0xc0, 0x99,
-	0x6a, 0x5a, 0xad, 0x59, 0x77, 0xd9, 0x18, 0x76, 0x50, 0x77, 0x8d, 0xfc, 0x02, 0xc0, 0x7e, 0x4e,
-	0x6e, 0x95, 0x51, 0xe6, 0x0d, 0x4b, 0x78, 0x15, 0xf1, 0xd5, 0x22, 0xbe, 0xed, 0x0d, 0x0b, 0x2b,
-	0x5c, 0x86, 0xcb, 0xfe, 0x27, 0xf0, 0xca, 0xe1, 0x7f, 0x17, 0xc3, 0xdf, 0xa0, 0x7e, 0x63, 0x78,
-	0x8e, 0x5e, 0x0d, 0x9f, 0x2e, 0x86, 0x1b, 0xbc, 0x1c, 0xbe, 0x49, 0x9c, 0x7c, 0x85, 0x40, 0xc9,
-	0x34, 0x2e, 0xc1, 0x33, 0x08, 0x2f, 0x19, 0xfd, 0x65, 0x26, 0x17, 0xc0, 0x2d, 0xf2, 0x7f, 0x09,
-	0xbc, 0x9a, 0xfc, 0x0f, 0xc2, 0x4e, 0x01, 0x2e, 0x07, 0x3f, 0x24, 0x2b, 0xd9, 0xe5, 0x98, 0x43,
-	0x4b, 0x68, 0x0c, 0x8a, 0x32, 0xcf, 0x93, 0xa9, 0xd0, 0xce, 0x6c, 0xd3, 0x6a, 0xd5, 0xdc, 0xc5,
-	0x88, 0x8d, 0xcc, 0x71, 0x25, 0x3d, 0x50, 0xdb, 0x46, 0xb3, 0xb7, 0xc8, 0x7f, 0x3e, 0x4f, 0x3c,
-	0x29, 0x34, 0x17, 0x29, 0x50, 0x2c, 0x72, 0x11, 0xd0, 0x63, 0x2e, 0x7c, 0x79, 0xec, 0x10, 0xbc,
-	0xde, 0xd5, 0x82, 0xa5, 0x9b, 0x3b, 0x3e, 0xa0, 0xc1, 0x7e, 0x40, 0x96, 0x8b, 0x7c, 0x7e, 0x6c,
-	0x11, 0x1b, 0x39, 0x73, 0x88, 0x2e, 0x16, 0x54, 0x73, 0x5e, 0xaf, 0xd8, 0xe8, 0x2a, 0x95, 0xdf,
-	0x74, 0x46, 0xfd, 0x7b, 0x8d, 0x32, 0x3d, 0x67, 0xd4, 0x33, 0xb2, 0x56, 0xee, 0x55, 0x1c, 0x72,
-	0x15, 0x65, 0x5b, 0xe5, 0xd2, 0x77, 0x6a, 0x4d, 0xab, 0x35, 0xe5, 0x3a, 0xa5, 0x56, 0xd1, 0xd0,
-	0x43, 0xdd, 0x7e, 0x44, 0x8a, 0x1a, 0xf5, 0x21, 0x04, 0xcd, 0xa5, 0xc0, 0xd4, 0x79, 0x4c, 0x2d,
-	0xf6, 0xb4, 0x9b, 0xcb, 0x59, 0xee, 0x26, 0x71, 0x12, 0xcd, 0x42, 0xa0, 0xb1, 0x0c, 0xb9, 0x37,
-	0xa6, 0x5e, 0x08, 0x4c, 0xa4, 0x31, 0x92, 0x75, 0x24, 0x97, 0x50, 0xef, 0xa1, 0xdc, 0x35, 0x6a,
-	0x06, 0xe2, 0x9f, 0x28, 0xe8, 0xe7, 0x54, 0x6a, 0x46, 0xd3, 0xd8, 0x67, 0x1a, 0x28, 0x17, 0x1a,
-	0xd4, 0x11, 0x0b, 0x9d, 0x05, 0x93, 0x19, 0x71, 0xf1, 0x36, 0xd3, 0xdf, 0xa3, 0xbc, 0x97, 0xab,
-	0x4f, 0xaa, 0x5f, 0xbf, 0xad, 0x57, 0x36, 0x7e, 0x5a, 0xa4, 0x7e, 0x70, 0xf3, 0x14, 0x26, 0x10,
-	0x44, 0x20, 0xb4, 0x99, 0x42, 0x6b, 0x32, 0x85, 0xfb, 0xa6, 0x8c, 0x53, 0xb8, 0x49, 0x1c, 0x05,
-	0x7e, 0x2a, 0x7c, 0x26, 0x34, 0xf5, 0x99, 0x66, 0xd4, 0x1b, 0xa4, 0x62, 0x48, 0x45, 0x1a, 0xe1,
-	0xdc, 0xd6, 0xdc, 0xa5, 0x89, 0xbe, 0xcb, 0x34, 0xeb, 0x66, 0xea, 0xeb, 0x34, 0xb2, 0x9f, 0x92,
-	0xb5, 0x4b, 0x30, 0x66, 0x8a, 0xeb, 0x71, 0x01, 0x9d, 0x42, 0x74, 0x65, 0xe2, 0xe8, 0xa1, 0x61,
-	0x02, 0xdf, 0x21, 0xf5, 0x6c, 0xd3, 0xde, 0x80, 0xa9, 0x00, 0x4c, 0x7b, 0x55, 0x6c, 0xaf, 0x16,
-	0x71, 0xd1, 0xc5, 0x6a, 0xd6, 0x9d, 0xd9, 0xe1, 0xce, 0xde, 0xe9, 0x79, 0xc3, 0x3a, 0x3b, 0x6f,
-	0x58, 0xbf, 0xce, 0x1b, 0xd6, 0x97, 0x8b, 0x46, 0xe5, 0xec, 0xa2, 0x51, 0xf9, 0x7e, 0xd1, 0xa8,
-	0x7c, 0xec, 0x04, 0x5c, 0x0f, 0xd2, 0x7e, 0xdb, 0x93, 0x51, 0xa7, 0x2f, 0xfa, 0x77, 0xbd, 0x01,
-	0xe3, 0xa2, 0x53, 0x78, 0x2e, 0x47, 0x93, 0x07, 0x53, 0x8f, 0x63, 0x48, 0xfa, 0xd3, 0xf8, 0x0c,
-	0xde, 0xff, 0x1d, 0x00, 0x00, 0xff, 0xff, 0xf9, 0xde, 0x9a, 0xf5, 0x53, 0x05, 0x00, 0x00,
+	// 745 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x54, 0x5d, 0x4f, 0xdb, 0x3a,
+	0x18, 0x6e, 0x0e, 0x3d, 0x1c, 0x30, 0xa7, 0x94, 0x13, 0xc1, 0x21, 0x7c, 0xac, 0x74, 0x4c, 0x42,
+	0xbd, 0x59, 0x2b, 0xed, 0x43, 0xec, 0x4b, 0x68, 0x50, 0x36, 0x84, 0x34, 0x58, 0x57, 0x36, 0x26,
+	0xed, 0xc6, 0x72, 0x1c, 0x93, 0x7a, 0x4d, 0xec, 0xcc, 0x71, 0x4a, 0xcb, 0xaf, 0xd8, 0xe5, 0x2e,
+	0xf7, 0x73, 0xb8, 0xd8, 0x05, 0x97, 0xbb, 0xda, 0x26, 0xf8, 0x23, 0x93, 0xed, 0xac, 0x24, 0xed,
+	0x76, 0x17, 0xbd, 0xcf, 0x87, 0x1f, 0xbf, 0xb1, 0x1e, 0xb0, 0xe6, 0x0b, 0x42, 0xd8, 0x09, 0x25,
+	0x81, 0xd7, 0x88, 0x25, 0x17, 0xc8, 0x27, 0x8d, 0x08, 0x09, 0x14, 0xc6, 0xf5, 0x48, 0x70, 0xc9,
+	0x6d, 0xfb, 0x9a, 0x50, 0x4f, 0x09, 0xcb, 0xf3, 0x3e, 0xf7, 0xb9, 0x86, 0x1b, 0xea, 0xcb, 0x30,
+	0xd7, 0xbf, 0x4c, 0x81, 0xc9, 0x96, 0x96, 0xda, 0xaf, 0xc1, 0x5c, 0x8f, 0x88, 0x98, 0x72, 0x46,
+	0x3c, 0x68, 0xec, 0x1c, 0xab, 0x6a, 0xd5, 0x66, 0xee, 0xdc, 0xaa, 0x8f, 0xfb, 0xd5, 0x8f, 0x7f,
+	0x71, 0x8d, 0x7c, 0xa7, 0x78, 0xfe, 0x6d, 0xad, 0xd0, 0x2e, 0xf7, 0xf2, 0x63, 0xbb, 0x06, 0xe6,
+	0x42, 0xd4, 0x87, 0x11, 0x1a, 0x04, 0x1c, 0x79, 0x30, 0xa6, 0x67, 0xc4, 0xf9, 0xab, 0x6a, 0xd5,
+	0x8a, 0xed, 0xd9, 0x10, 0xf5, 0x5b, 0x66, 0x7c, 0x44, 0xcf, 0x88, 0xfd, 0x14, 0xdc, 0x70, 0x63,
+	0x0c, 0x43, 0x2a, 0x04, 0x17, 0xd0, 0x4d, 0x70, 0x97, 0x48, 0x28, 0x48, 0x80, 0x06, 0x44, 0xc0,
+	0x13, 0x42, 0x9c, 0x89, 0xaa, 0x55, 0x9b, 0x6e, 0x2f, 0xb9, 0x31, 0x3e, 0xd0, 0x9c, 0x1d, 0x4d,
+	0x69, 0x1b, 0xc6, 0x73, 0x42, 0xec, 0x3d, 0x70, 0x73, 0xdc, 0x01, 0xe1, 0x6e, 0xce, 0xa5, 0xa8,
+	0x5d, 0x56, 0x47, 0x5c, 0xb6, 0x71, 0x37, 0x63, 0x94, 0x8f, 0xc2, 0xdd, 0xf7, 0x04, 0xe7, 0xa3,
+	0xfc, 0x3d, 0x12, 0xe5, 0xa5, 0xa6, 0xfc, 0x31, 0x4a, 0xea, 0x30, 0x1a, 0x65, 0x72, 0x24, 0x8a,
+	0x71, 0xc9, 0x47, 0xd9, 0x02, 0xab, 0x19, 0x23, 0x5f, 0xf0, 0x24, 0xca, 0x79, 0xfc, 0xa3, 0x3d,
+	0x9c, 0xa1, 0xc7, 0x9e, 0x62, 0x64, 0xf4, 0xcf, 0x40, 0x75, 0x4c, 0x3f, 0x9a, 0x63, 0x4a, 0x7b,
+	0xac, 0xe4, 0x3d, 0xf2, 0x31, 0xee, 0x83, 0x45, 0xf5, 0x1b, 0xcd, 0x4e, 0x63, 0x18, 0x11, 0x01,
+	0x11, 0xc6, 0x3c, 0x61, 0xd2, 0x99, 0xae, 0x5a, 0xb5, 0x52, 0x7b, 0x3e, 0x44, 0x7d, 0xb3, 0xca,
+	0xb8, 0x45, 0xc4, 0xb6, 0xc1, 0xec, 0x2d, 0xb0, 0xe2, 0xd1, 0x18, 0x73, 0x26, 0x29, 0x4b, 0x08,
+	0xd4, 0x43, 0xca, 0x7c, 0x78, 0x4a, 0x99, 0xc7, 0x4f, 0x1d, 0xa0, 0x1f, 0xc2, 0x52, 0x86, 0xd2,
+	0x4c, 0x19, 0x6f, 0x35, 0xc1, 0xbe, 0x07, 0xfe, 0xcf, 0xea, 0xd3, 0x3d, 0x86, 0xa8, 0xef, 0xcc,
+	0x68, 0xe9, 0x7c, 0x06, 0x35, 0xdb, 0x3b, 0x40, 0xfd, 0x51, 0x55, 0xfa, 0x10, 0x94, 0xea, 0xdf,
+	0x31, 0x95, 0xc9, 0xac, 0x54, 0x4f, 0xc0, 0x72, 0x3e, 0x2b, 0x3b, 0xa1, 0x22, 0x54, 0x57, 0xa5,
+	0xdc, 0x73, 0x4a, 0x55, 0xab, 0x36, 0xd1, 0x76, 0x72, 0x51, 0x35, 0xa1, 0xa5, 0x71, 0xfb, 0x01,
+	0xc8, 0x62, 0xd0, 0x23, 0x01, 0x91, 0x94, 0x33, 0x7d, 0xea, 0xac, 0x3e, 0x35, 0x9b, 0x69, 0x37,
+	0x85, 0xd5, 0xb9, 0x9b, 0xc0, 0x89, 0x25, 0x0a, 0x08, 0x8c, 0x78, 0x40, 0xf1, 0x00, 0xe2, 0x80,
+	0x20, 0x96, 0x44, 0x5a, 0x59, 0xd6, 0xca, 0x05, 0x8d, 0xb7, 0x34, 0xdc, 0x34, 0xa8, 0x12, 0x3e,
+	0x04, 0x4b, 0x21, 0x65, 0xf0, 0x43, 0xc2, 0x25, 0x82, 0x49, 0xe4, 0x21, 0x49, 0x20, 0x65, 0x92,
+	0x88, 0x1e, 0x0a, 0x9c, 0x39, 0x73, 0x66, 0x48, 0xd9, 0x2b, 0x85, 0xbf, 0xd1, 0xf0, 0x7e, 0x8a,
+	0xda, 0x2d, 0xb0, 0xa1, 0x7e, 0x67, 0xc0, 0x31, 0x0a, 0x60, 0x8f, 0x0a, 0x99, 0xa0, 0x20, 0x7d,
+	0x1c, 0x2c, 0xd1, 0x77, 0x4e, 0xb7, 0xe6, 0xfc, 0xa7, 0xff, 0x6e, 0x35, 0x44, 0xfd, 0x17, 0x8a,
+	0x7c, 0x6c, 0xb8, 0xfa, 0x85, 0x1c, 0x26, 0xea, 0xf2, 0x66, 0x81, 0x8f, 0x8a, 0x9f, 0x3e, 0xaf,
+	0x15, 0xd6, 0xbf, 0x5b, 0xa0, 0x7c, 0xfc, 0xfb, 0x06, 0x88, 0x89, 0x1f, 0x12, 0x26, 0x4d, 0x03,
+	0x58, 0xc3, 0x06, 0x38, 0x32, 0x63, 0xdd, 0x00, 0x9b, 0xc0, 0x11, 0xc4, 0x4b, 0x98, 0x87, 0x98,
+	0x84, 0x1e, 0x92, 0x08, 0xe2, 0x4e, 0xc2, 0xba, 0x2a, 0x92, 0xee, 0x8c, 0x52, 0x7b, 0x61, 0x88,
+	0xef, 0x22, 0x89, 0x9a, 0x0a, 0x3d, 0x4c, 0x42, 0xfb, 0x31, 0x58, 0xbe, 0x16, 0x46, 0x48, 0x50,
+	0x39, 0xc8, 0x48, 0x27, 0xb4, 0x74, 0x71, 0xc8, 0x68, 0x69, 0xc2, 0x50, 0xbc, 0x01, 0xca, 0x6a,
+	0x8d, 0xb8, 0x83, 0x84, 0x4f, 0x4c, 0xbc, 0xa2, 0x8e, 0x57, 0x0a, 0x29, 0x6b, 0xea, 0xa9, 0x4a,
+	0x67, 0x6e, 0xb8, 0xb3, 0x7f, 0x7e, 0x59, 0xb1, 0x2e, 0x2e, 0x2b, 0xd6, 0x8f, 0xcb, 0x8a, 0xf5,
+	0xf1, 0xaa, 0x52, 0xb8, 0xb8, 0xaa, 0x14, 0xbe, 0x5e, 0x55, 0x0a, 0xef, 0x1a, 0x3e, 0x95, 0x9d,
+	0xc4, 0xad, 0x63, 0x1e, 0x36, 0x5c, 0xe6, 0xde, 0xc6, 0x1d, 0x44, 0x59, 0x23, 0x53, 0xd5, 0xfd,
+	0x61, 0x59, 0xcb, 0x41, 0x44, 0x62, 0x77, 0x52, 0x57, 0xf0, 0xdd, 0x9f, 0x01, 0x00, 0x00, 0xff,
+	0xff, 0xe9, 0x0b, 0xdd, 0x21, 0xcf, 0x05, 0x00, 0x00,
 }
 
 func (m *Params) Marshal() (dAtA []byte, err error) {
@@ -348,6 +361,13 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	_ = i
 	var l int
 	_ = l
+	if m.MaxLocalVirtualGroupNumPerBucket != 0 {
+		i = encodeVarintParams(dAtA, i, uint64(m.MaxLocalVirtualGroupNumPerBucket))
+		i--
+		dAtA[i] = 0x1
+		i--
+		dAtA[i] = 0x88
+	}
 	if m.MinQuotaUpdateInterval != 0 {
 		i = encodeVarintParams(dAtA, i, uint64(m.MinQuotaUpdateInterval))
 		i--
@@ -390,45 +410,45 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		i--
 		dAtA[i] = 0x48
 	}
-	if len(m.MirrorGroupAckRelayerFee) > 0 {
-		i -= len(m.MirrorGroupAckRelayerFee)
-		copy(dAtA[i:], m.MirrorGroupAckRelayerFee)
-		i = encodeVarintParams(dAtA, i, uint64(len(m.MirrorGroupAckRelayerFee)))
+	if len(m.BscMirrorGroupAckRelayerFee) > 0 {
+		i -= len(m.BscMirrorGroupAckRelayerFee)
+		copy(dAtA[i:], m.BscMirrorGroupAckRelayerFee)
+		i = encodeVarintParams(dAtA, i, uint64(len(m.BscMirrorGroupAckRelayerFee)))
 		i--
 		dAtA[i] = 0x42
 	}
-	if len(m.MirrorGroupRelayerFee) > 0 {
-		i -= len(m.MirrorGroupRelayerFee)
-		copy(dAtA[i:], m.MirrorGroupRelayerFee)
-		i = encodeVarintParams(dAtA, i, uint64(len(m.MirrorGroupRelayerFee)))
+	if len(m.BscMirrorGroupRelayerFee) > 0 {
+		i -= len(m.BscMirrorGroupRelayerFee)
+		copy(dAtA[i:], m.BscMirrorGroupRelayerFee)
+		i = encodeVarintParams(dAtA, i, uint64(len(m.BscMirrorGroupRelayerFee)))
 		i--
 		dAtA[i] = 0x3a
 	}
-	if len(m.MirrorObjectAckRelayerFee) > 0 {
-		i -= len(m.MirrorObjectAckRelayerFee)
-		copy(dAtA[i:], m.MirrorObjectAckRelayerFee)
-		i = encodeVarintParams(dAtA, i, uint64(len(m.MirrorObjectAckRelayerFee)))
+	if len(m.BscMirrorObjectAckRelayerFee) > 0 {
+		i -= len(m.BscMirrorObjectAckRelayerFee)
+		copy(dAtA[i:], m.BscMirrorObjectAckRelayerFee)
+		i = encodeVarintParams(dAtA, i, uint64(len(m.BscMirrorObjectAckRelayerFee)))
 		i--
 		dAtA[i] = 0x32
 	}
-	if len(m.MirrorObjectRelayerFee) > 0 {
-		i -= len(m.MirrorObjectRelayerFee)
-		copy(dAtA[i:], m.MirrorObjectRelayerFee)
-		i = encodeVarintParams(dAtA, i, uint64(len(m.MirrorObjectRelayerFee)))
+	if len(m.BscMirrorObjectRelayerFee) > 0 {
+		i -= len(m.BscMirrorObjectRelayerFee)
+		copy(dAtA[i:], m.BscMirrorObjectRelayerFee)
+		i = encodeVarintParams(dAtA, i, uint64(len(m.BscMirrorObjectRelayerFee)))
 		i--
 		dAtA[i] = 0x2a
 	}
-	if len(m.MirrorBucketAckRelayerFee) > 0 {
-		i -= len(m.MirrorBucketAckRelayerFee)
-		copy(dAtA[i:], m.MirrorBucketAckRelayerFee)
-		i = encodeVarintParams(dAtA, i, uint64(len(m.MirrorBucketAckRelayerFee)))
+	if len(m.BscMirrorBucketAckRelayerFee) > 0 {
+		i -= len(m.BscMirrorBucketAckRelayerFee)
+		copy(dAtA[i:], m.BscMirrorBucketAckRelayerFee)
+		i = encodeVarintParams(dAtA, i, uint64(len(m.BscMirrorBucketAckRelayerFee)))
 		i--
 		dAtA[i] = 0x22
 	}
-	if len(m.MirrorBucketRelayerFee) > 0 {
-		i -= len(m.MirrorBucketRelayerFee)
-		copy(dAtA[i:], m.MirrorBucketRelayerFee)
-		i = encodeVarintParams(dAtA, i, uint64(len(m.MirrorBucketRelayerFee)))
+	if len(m.BscMirrorBucketRelayerFee) > 0 {
+		i -= len(m.BscMirrorBucketRelayerFee)
+		copy(dAtA[i:], m.BscMirrorBucketRelayerFee)
+		i = encodeVarintParams(dAtA, i, uint64(len(m.BscMirrorBucketRelayerFee)))
 		i--
 		dAtA[i] = 0x1a
 	}
@@ -515,27 +535,27 @@ func (m *Params) Size() (n int) {
 	if m.MaxPayloadSize != 0 {
 		n += 1 + sovParams(uint64(m.MaxPayloadSize))
 	}
-	l = len(m.MirrorBucketRelayerFee)
+	l = len(m.BscMirrorBucketRelayerFee)
 	if l > 0 {
 		n += 1 + l + sovParams(uint64(l))
 	}
-	l = len(m.MirrorBucketAckRelayerFee)
+	l = len(m.BscMirrorBucketAckRelayerFee)
 	if l > 0 {
 		n += 1 + l + sovParams(uint64(l))
 	}
-	l = len(m.MirrorObjectRelayerFee)
+	l = len(m.BscMirrorObjectRelayerFee)
 	if l > 0 {
 		n += 1 + l + sovParams(uint64(l))
 	}
-	l = len(m.MirrorObjectAckRelayerFee)
+	l = len(m.BscMirrorObjectAckRelayerFee)
 	if l > 0 {
 		n += 1 + l + sovParams(uint64(l))
 	}
-	l = len(m.MirrorGroupRelayerFee)
+	l = len(m.BscMirrorGroupRelayerFee)
 	if l > 0 {
 		n += 1 + l + sovParams(uint64(l))
 	}
-	l = len(m.MirrorGroupAckRelayerFee)
+	l = len(m.BscMirrorGroupAckRelayerFee)
 	if l > 0 {
 		n += 1 + l + sovParams(uint64(l))
 	}
@@ -563,6 +583,9 @@ func (m *Params) Size() (n int) {
 	if m.MinQuotaUpdateInterval != 0 {
 		n += 2 + sovParams(uint64(m.MinQuotaUpdateInterval))
 	}
+	if m.MaxLocalVirtualGroupNumPerBucket != 0 {
+		n += 2 + sovParams(uint64(m.MaxLocalVirtualGroupNumPerBucket))
+	}
 	return n
 }
 
@@ -676,7 +699,7 @@ func (m *Params) Unmarshal(dAtA []byte) error {
 			}
 		case 3:
 			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field MirrorBucketRelayerFee", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field BscMirrorBucketRelayerFee", wireType)
 			}
 			var stringLen uint64
 			for shift := uint(0); ; shift += 7 {
@@ -704,11 +727,11 @@ func (m *Params) Unmarshal(dAtA []byte) error {
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			m.MirrorBucketRelayerFee = string(dAtA[iNdEx:postIndex])
+			m.BscMirrorBucketRelayerFee = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
 		case 4:
 			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field MirrorBucketAckRelayerFee", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field BscMirrorBucketAckRelayerFee", wireType)
 			}
 			var stringLen uint64
 			for shift := uint(0); ; shift += 7 {
@@ -736,11 +759,11 @@ func (m *Params) Unmarshal(dAtA []byte) error {
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			m.MirrorBucketAckRelayerFee = string(dAtA[iNdEx:postIndex])
+			m.BscMirrorBucketAckRelayerFee = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
 		case 5:
 			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field MirrorObjectRelayerFee", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field BscMirrorObjectRelayerFee", wireType)
 			}
 			var stringLen uint64
 			for shift := uint(0); ; shift += 7 {
@@ -768,11 +791,11 @@ func (m *Params) Unmarshal(dAtA []byte) error {
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			m.MirrorObjectRelayerFee = string(dAtA[iNdEx:postIndex])
+			m.BscMirrorObjectRelayerFee = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
 		case 6:
 			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field MirrorObjectAckRelayerFee", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field BscMirrorObjectAckRelayerFee", wireType)
 			}
 			var stringLen uint64
 			for shift := uint(0); ; shift += 7 {
@@ -800,11 +823,11 @@ func (m *Params) Unmarshal(dAtA []byte) error {
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			m.MirrorObjectAckRelayerFee = string(dAtA[iNdEx:postIndex])
+			m.BscMirrorObjectAckRelayerFee = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
 		case 7:
 			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field MirrorGroupRelayerFee", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field BscMirrorGroupRelayerFee", wireType)
 			}
 			var stringLen uint64
 			for shift := uint(0); ; shift += 7 {
@@ -832,11 +855,11 @@ func (m *Params) Unmarshal(dAtA []byte) error {
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			m.MirrorGroupRelayerFee = string(dAtA[iNdEx:postIndex])
+			m.BscMirrorGroupRelayerFee = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
 		case 8:
 			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field MirrorGroupAckRelayerFee", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field BscMirrorGroupAckRelayerFee", wireType)
 			}
 			var stringLen uint64
 			for shift := uint(0); ; shift += 7 {
@@ -864,7 +887,7 @@ func (m *Params) Unmarshal(dAtA []byte) error {
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			m.MirrorGroupAckRelayerFee = string(dAtA[iNdEx:postIndex])
+			m.BscMirrorGroupAckRelayerFee = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
 		case 9:
 			if wireType != 0 {
@@ -1018,6 +1041,25 @@ func (m *Params) Unmarshal(dAtA []byte) error {
 					break
 				}
 			}
+		case 17:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field MaxLocalVirtualGroupNumPerBucket", wireType)
+			}
+			m.MaxLocalVirtualGroupNumPerBucket = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowParams
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.MaxLocalVirtualGroupNumPerBucket |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
 		default:
 			iNdEx = preIndex
 			skippy, err := skipParams(dAtA[iNdEx:])
diff --git a/x/storage/types/query.pb.go b/x/storage/types/query.pb.go
index ad9f66ea3..ee814f80c 100644
--- a/x/storage/types/query.pb.go
+++ b/x/storage/types/query.pb.go
@@ -6,8 +6,10 @@ package types
 import (
 	context "context"
 	fmt "fmt"
-	types "github.com/bnb-chain/greenfield/x/permission/types"
+	types1 "github.com/bnb-chain/greenfield/x/permission/types"
+	types "github.com/bnb-chain/greenfield/x/virtualgroup/types"
 	_ "github.com/cosmos/cosmos-proto"
+	github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types"
 	query "github.com/cosmos/cosmos-sdk/types/query"
 	_ "github.com/cosmos/gogoproto/gogoproto"
 	grpc1 "github.com/cosmos/gogoproto/grpc"
@@ -436,7 +438,8 @@ func (m *QueryHeadObjectByIdRequest) GetObjectId() string {
 }
 
 type QueryHeadObjectResponse struct {
-	ObjectInfo *ObjectInfo `protobuf:"bytes,1,opt,name=object_info,json=objectInfo,proto3" json:"object_info,omitempty"`
+	ObjectInfo         *ObjectInfo               `protobuf:"bytes,1,opt,name=object_info,json=objectInfo,proto3" json:"object_info,omitempty"`
+	GlobalVirtualGroup *types.GlobalVirtualGroup `protobuf:"bytes,2,opt,name=global_virtual_group,json=globalVirtualGroup,proto3" json:"global_virtual_group,omitempty"`
 }
 
 func (m *QueryHeadObjectResponse) Reset()         { *m = QueryHeadObjectResponse{} }
@@ -479,6 +482,13 @@ func (m *QueryHeadObjectResponse) GetObjectInfo() *ObjectInfo {
 	return nil
 }
 
+func (m *QueryHeadObjectResponse) GetGlobalVirtualGroup() *types.GlobalVirtualGroup {
+	if m != nil {
+		return m.GlobalVirtualGroup
+	}
+	return nil
+}
+
 type QueryListBucketsRequest struct {
 	Pagination *query.PageRequest `protobuf:"bytes,1,opt,name=pagination,proto3" json:"pagination,omitempty"`
 }
@@ -960,7 +970,7 @@ func (m *QueryPolicyForAccountRequest) GetPrincipalAddress() string {
 }
 
 type QueryPolicyForAccountResponse struct {
-	Policy *types.Policy `protobuf:"bytes,1,opt,name=policy,proto3" json:"policy,omitempty"`
+	Policy *types1.Policy `protobuf:"bytes,1,opt,name=policy,proto3" json:"policy,omitempty"`
 }
 
 func (m *QueryPolicyForAccountResponse) Reset()         { *m = QueryPolicyForAccountResponse{} }
@@ -996,7 +1006,7 @@ func (m *QueryPolicyForAccountResponse) XXX_DiscardUnknown() {
 
 var xxx_messageInfo_QueryPolicyForAccountResponse proto.InternalMessageInfo
 
-func (m *QueryPolicyForAccountResponse) GetPolicy() *types.Policy {
+func (m *QueryPolicyForAccountResponse) GetPolicy() *types1.Policy {
 	if m != nil {
 		return m.Policy
 	}
@@ -1004,10 +1014,10 @@ func (m *QueryPolicyForAccountResponse) GetPolicy() *types.Policy {
 }
 
 type QueryVerifyPermissionRequest struct {
-	Operator   string           `protobuf:"bytes,1,opt,name=operator,proto3" json:"operator,omitempty"`
-	BucketName string           `protobuf:"bytes,2,opt,name=bucket_name,json=bucketName,proto3" json:"bucket_name,omitempty"`
-	ObjectName string           `protobuf:"bytes,3,opt,name=object_name,json=objectName,proto3" json:"object_name,omitempty"`
-	ActionType types.ActionType `protobuf:"varint,4,opt,name=action_type,json=actionType,proto3,enum=greenfield.permission.ActionType" json:"action_type,omitempty"`
+	Operator   string            `protobuf:"bytes,1,opt,name=operator,proto3" json:"operator,omitempty"`
+	BucketName string            `protobuf:"bytes,2,opt,name=bucket_name,json=bucketName,proto3" json:"bucket_name,omitempty"`
+	ObjectName string            `protobuf:"bytes,3,opt,name=object_name,json=objectName,proto3" json:"object_name,omitempty"`
+	ActionType types1.ActionType `protobuf:"varint,4,opt,name=action_type,json=actionType,proto3,enum=greenfield.permission.ActionType" json:"action_type,omitempty"`
 }
 
 func (m *QueryVerifyPermissionRequest) Reset()         { *m = QueryVerifyPermissionRequest{} }
@@ -1064,15 +1074,15 @@ func (m *QueryVerifyPermissionRequest) GetObjectName() string {
 	return ""
 }
 
-func (m *QueryVerifyPermissionRequest) GetActionType() types.ActionType {
+func (m *QueryVerifyPermissionRequest) GetActionType() types1.ActionType {
 	if m != nil {
 		return m.ActionType
 	}
-	return types.ACTION_UNSPECIFIED
+	return types1.ACTION_UNSPECIFIED
 }
 
 type QueryVerifyPermissionResponse struct {
-	Effect types.Effect `protobuf:"varint,1,opt,name=effect,proto3,enum=greenfield.permission.Effect" json:"effect,omitempty"`
+	Effect types1.Effect `protobuf:"varint,1,opt,name=effect,proto3,enum=greenfield.permission.Effect" json:"effect,omitempty"`
 }
 
 func (m *QueryVerifyPermissionResponse) Reset()         { *m = QueryVerifyPermissionResponse{} }
@@ -1108,11 +1118,11 @@ func (m *QueryVerifyPermissionResponse) XXX_DiscardUnknown() {
 
 var xxx_messageInfo_QueryVerifyPermissionResponse proto.InternalMessageInfo
 
-func (m *QueryVerifyPermissionResponse) GetEffect() types.Effect {
+func (m *QueryVerifyPermissionResponse) GetEffect() types1.Effect {
 	if m != nil {
 		return m.Effect
 	}
-	return types.EFFECT_UNSPECIFIED
+	return types1.EFFECT_UNSPECIFIED
 }
 
 type QueryHeadGroupRequest struct {
@@ -1376,7 +1386,7 @@ func (m *QueryHeadGroupMemberRequest) GetGroupName() string {
 }
 
 type QueryHeadGroupMemberResponse struct {
-	GroupMember *types.GroupMember `protobuf:"bytes,1,opt,name=group_member,json=groupMember,proto3" json:"group_member,omitempty"`
+	GroupMember *types1.GroupMember `protobuf:"bytes,1,opt,name=group_member,json=groupMember,proto3" json:"group_member,omitempty"`
 }
 
 func (m *QueryHeadGroupMemberResponse) Reset()         { *m = QueryHeadGroupMemberResponse{} }
@@ -1412,7 +1422,7 @@ func (m *QueryHeadGroupMemberResponse) XXX_DiscardUnknown() {
 
 var xxx_messageInfo_QueryHeadGroupMemberResponse proto.InternalMessageInfo
 
-func (m *QueryHeadGroupMemberResponse) GetGroupMember() *types.GroupMember {
+func (m *QueryHeadGroupMemberResponse) GetGroupMember() *types1.GroupMember {
 	if m != nil {
 		return m.GroupMember
 	}
@@ -1472,7 +1482,7 @@ func (m *QueryPolicyForGroupRequest) GetPrincipalGroupId() string {
 }
 
 type QueryPolicyForGroupResponse struct {
-	Policy *types.Policy `protobuf:"bytes,1,opt,name=policy,proto3" json:"policy,omitempty"`
+	Policy *types1.Policy `protobuf:"bytes,1,opt,name=policy,proto3" json:"policy,omitempty"`
 }
 
 func (m *QueryPolicyForGroupResponse) Reset()         { *m = QueryPolicyForGroupResponse{} }
@@ -1508,7 +1518,7 @@ func (m *QueryPolicyForGroupResponse) XXX_DiscardUnknown() {
 
 var xxx_messageInfo_QueryPolicyForGroupResponse proto.InternalMessageInfo
 
-func (m *QueryPolicyForGroupResponse) GetPolicy() *types.Policy {
+func (m *QueryPolicyForGroupResponse) GetPolicy() *types1.Policy {
 	if m != nil {
 		return m.Policy
 	}
@@ -1560,7 +1570,7 @@ func (m *QueryPolicyByIdRequest) GetPolicyId() string {
 }
 
 type QueryPolicyByIdResponse struct {
-	Policy *types.Policy `protobuf:"bytes,1,opt,name=policy,proto3" json:"policy,omitempty"`
+	Policy *types1.Policy `protobuf:"bytes,1,opt,name=policy,proto3" json:"policy,omitempty"`
 }
 
 func (m *QueryPolicyByIdResponse) Reset()         { *m = QueryPolicyByIdResponse{} }
@@ -1596,13 +1606,113 @@ func (m *QueryPolicyByIdResponse) XXX_DiscardUnknown() {
 
 var xxx_messageInfo_QueryPolicyByIdResponse proto.InternalMessageInfo
 
-func (m *QueryPolicyByIdResponse) GetPolicy() *types.Policy {
+func (m *QueryPolicyByIdResponse) GetPolicy() *types1.Policy {
 	if m != nil {
 		return m.Policy
 	}
 	return nil
 }
 
+type QueryLockFeeRequest struct {
+	// primary_sp_address is the address of the primary sp.
+	PrimarySpAddress string `protobuf:"bytes,1,opt,name=primary_sp_address,json=primarySpAddress,proto3" json:"primary_sp_address,omitempty"`
+	// create_at define the block timestamp when the object created.
+	CreateAt int64 `protobuf:"varint,2,opt,name=create_at,json=createAt,proto3" json:"create_at,omitempty"`
+	// payloadSize is the total size of the object payload
+	PayloadSize uint64 `protobuf:"varint,3,opt,name=payload_size,json=payloadSize,proto3" json:"payload_size,omitempty"`
+}
+
+func (m *QueryLockFeeRequest) Reset()         { *m = QueryLockFeeRequest{} }
+func (m *QueryLockFeeRequest) String() string { return proto.CompactTextString(m) }
+func (*QueryLockFeeRequest) ProtoMessage()    {}
+func (*QueryLockFeeRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_b1b80b580af04cb0, []int{33}
+}
+func (m *QueryLockFeeRequest) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *QueryLockFeeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_QueryLockFeeRequest.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *QueryLockFeeRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_QueryLockFeeRequest.Merge(m, src)
+}
+func (m *QueryLockFeeRequest) XXX_Size() int {
+	return m.Size()
+}
+func (m *QueryLockFeeRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_QueryLockFeeRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryLockFeeRequest proto.InternalMessageInfo
+
+func (m *QueryLockFeeRequest) GetPrimarySpAddress() string {
+	if m != nil {
+		return m.PrimarySpAddress
+	}
+	return ""
+}
+
+func (m *QueryLockFeeRequest) GetCreateAt() int64 {
+	if m != nil {
+		return m.CreateAt
+	}
+	return 0
+}
+
+func (m *QueryLockFeeRequest) GetPayloadSize() uint64 {
+	if m != nil {
+		return m.PayloadSize
+	}
+	return 0
+}
+
+type QueryLockFeeResponse struct {
+	Amount github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,1,opt,name=amount,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"amount"`
+}
+
+func (m *QueryLockFeeResponse) Reset()         { *m = QueryLockFeeResponse{} }
+func (m *QueryLockFeeResponse) String() string { return proto.CompactTextString(m) }
+func (*QueryLockFeeResponse) ProtoMessage()    {}
+func (*QueryLockFeeResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_b1b80b580af04cb0, []int{34}
+}
+func (m *QueryLockFeeResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *QueryLockFeeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_QueryLockFeeResponse.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *QueryLockFeeResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_QueryLockFeeResponse.Merge(m, src)
+}
+func (m *QueryLockFeeResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *QueryLockFeeResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_QueryLockFeeResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryLockFeeResponse proto.InternalMessageInfo
+
 func init() {
 	proto.RegisterType((*QueryParamsRequest)(nil), "greenfield.storage.QueryParamsRequest")
 	proto.RegisterType((*QueryParamsResponse)(nil), "greenfield.storage.QueryParamsResponse")
@@ -1637,121 +1747,137 @@ func init() {
 	proto.RegisterType((*QueryPolicyForGroupResponse)(nil), "greenfield.storage.QueryPolicyForGroupResponse")
 	proto.RegisterType((*QueryPolicyByIdRequest)(nil), "greenfield.storage.QueryPolicyByIdRequest")
 	proto.RegisterType((*QueryPolicyByIdResponse)(nil), "greenfield.storage.QueryPolicyByIdResponse")
+	proto.RegisterType((*QueryLockFeeRequest)(nil), "greenfield.storage.QueryLockFeeRequest")
+	proto.RegisterType((*QueryLockFeeResponse)(nil), "greenfield.storage.QueryLockFeeResponse")
 }
 
 func init() { proto.RegisterFile("greenfield/storage/query.proto", fileDescriptor_b1b80b580af04cb0) }
 
 var fileDescriptor_b1b80b580af04cb0 = []byte{
-	// 1743 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x59, 0xcd, 0x6f, 0x1c, 0x45,
-	0x16, 0x77, 0xdb, 0x59, 0xaf, 0x5d, 0xce, 0x26, 0xd9, 0xda, 0x64, 0xd7, 0xe9, 0xd8, 0x93, 0xa4,
-	0x57, 0x9b, 0x38, 0x59, 0x7b, 0xda, 0x4e, 0xe2, 0xd5, 0x5a, 0x21, 0x41, 0x1e, 0x62, 0x1b, 0x4b,
-	0x89, 0x63, 0x06, 0x2b, 0x08, 0x5f, 0x46, 0x35, 0x33, 0x35, 0x93, 0x26, 0x9e, 0xae, 0x49, 0x77,
-	0x3b, 0x30, 0x1a, 0xcd, 0x81, 0x9c, 0x38, 0x22, 0x22, 0xa4, 0x08, 0x71, 0x00, 0x21, 0x10, 0x70,
-	0xe1, 0xc2, 0x9d, 0x0b, 0x87, 0x48, 0x08, 0x29, 0x82, 0x0b, 0xe2, 0x80, 0x50, 0xc2, 0x1f, 0x82,
-	0xba, 0xea, 0x55, 0x77, 0xf5, 0xd7, 0xf4, 0x38, 0xf6, 0xad, 0xbb, 0xfa, 0xbd, 0x57, 0xbf, 0xf7,
-	0x51, 0xaf, 0xde, 0x6f, 0x06, 0x15, 0x9a, 0x0e, 0xa5, 0x76, 0xc3, 0xa2, 0x3b, 0x75, 0xd3, 0xf5,
-	0x98, 0x43, 0x9a, 0xd4, 0xbc, 0xbf, 0x4b, 0x9d, 0x4e, 0xb1, 0xed, 0x30, 0x8f, 0x61, 0x1c, 0x7e,
-	0x2f, 0xc2, 0x77, 0xfd, 0x62, 0x8d, 0xb9, 0x2d, 0xe6, 0x9a, 0x55, 0xe2, 0x82, 0xb0, 0xf9, 0x60,
-	0xa1, 0x4a, 0x3d, 0xb2, 0x60, 0xb6, 0x49, 0xd3, 0xb2, 0x89, 0x67, 0x31, 0x5b, 0xe8, 0xeb, 0x27,
-	0x85, 0x6c, 0x85, 0xbf, 0x99, 0xe2, 0x05, 0x3e, 0x1d, 0x6f, 0xb2, 0x26, 0x13, 0xeb, 0xfe, 0x13,
-	0xac, 0x4e, 0x35, 0x19, 0x6b, 0xee, 0x50, 0x93, 0xb4, 0x2d, 0x93, 0xd8, 0x36, 0xf3, 0xb8, 0x35,
-	0xa9, 0x63, 0x28, 0x70, 0xdb, 0xd4, 0x69, 0x59, 0xae, 0x6b, 0x31, 0xdb, 0xac, 0xb1, 0x56, 0x2b,
-	0xd8, 0xf2, 0x6c, 0xba, 0x8c, 0xd7, 0x69, 0x53, 0x69, 0xe6, 0x74, 0x8a, 0xd7, 0x6d, 0xe2, 0x90,
-	0x96, 0x14, 0x48, 0x0b, 0x8b, 0x62, 0xc0, 0x38, 0x8e, 0xf0, 0x6b, 0xbe, 0xe3, 0x9b, 0x5c, 0xa9,
-	0x4c, 0xef, 0xef, 0x52, 0xd7, 0x33, 0x6e, 0xa3, 0x7f, 0x44, 0x56, 0xdd, 0x36, 0xb3, 0x5d, 0x8a,
-	0xff, 0x8f, 0x46, 0x85, 0xf1, 0x49, 0xed, 0x8c, 0x36, 0x33, 0x71, 0x49, 0x2f, 0x26, 0x83, 0x5a,
-	0x14, 0x3a, 0xa5, 0x43, 0x4f, 0x7e, 0x3b, 0x3d, 0x54, 0x06, 0x79, 0xe3, 0x1a, 0x9a, 0x56, 0x0c,
-	0x96, 0x3a, 0x5b, 0x56, 0x8b, 0xba, 0x1e, 0x69, 0xb5, 0x61, 0x47, 0x3c, 0x85, 0xc6, 0x3d, 0xb9,
-	0xc6, 0xad, 0x8f, 0x94, 0xc3, 0x05, 0x63, 0x1b, 0x15, 0xb2, 0xd4, 0xf7, 0x0d, 0x6d, 0x09, 0xfd,
-	0x93, 0xdb, 0x7e, 0x95, 0x92, 0x7a, 0x69, 0xb7, 0x76, 0x8f, 0x7a, 0x12, 0xd3, 0x69, 0x34, 0x51,
-	0xe5, 0x0b, 0x15, 0x9b, 0xb4, 0x28, 0x37, 0x3c, 0x5e, 0x46, 0x62, 0x69, 0x83, 0xb4, 0xa8, 0xb1,
-	0x84, 0xf4, 0x98, 0x6a, 0xa9, 0xb3, 0x5e, 0x97, 0xea, 0xa7, 0xd0, 0x38, 0xa8, 0x5b, 0x75, 0x50,
-	0x1e, 0x13, 0x0b, 0xeb, 0x75, 0x63, 0x1b, 0xfd, 0x2b, 0xb1, 0x2b, 0xb8, 0xf2, 0x72, 0xb0, 0xad,
-	0x65, 0x37, 0x18, 0xf8, 0x53, 0x48, 0xf3, 0x47, 0x28, 0xae, 0xdb, 0x0d, 0x26, 0x61, 0xf9, 0xcf,
-	0xc6, 0xb6, 0xe2, 0xd1, 0xed, 0xea, 0x5b, 0xb4, 0x36, 0xb0, 0x47, 0xbe, 0x00, 0xe3, 0x1a, 0x42,
-	0x60, 0x58, 0x08, 0x88, 0xa5, 0x84, 0xcb, 0xc2, 0x76, 0xcc, 0x65, 0x50, 0x0f, 0x5d, 0x16, 0x0b,
-	0x31, 0x97, 0x25, 0xac, 0xd0, 0x65, 0xa9, 0x97, 0xe3, 0xb2, 0x50, 0x14, 0x2e, 0xb3, 0xe0, 0xd9,
-	0x20, 0x60, 0xfb, 0xa6, 0xe5, 0x7a, 0x22, 0x2a, 0xb2, 0x96, 0xf1, 0x2a, 0x42, 0xe1, 0x61, 0x06,
-	0xd3, 0xe7, 0x8a, 0x70, 0x80, 0xfd, 0x93, 0x5f, 0x14, 0x6d, 0x02, 0x4e, 0x7e, 0x71, 0x93, 0x34,
-	0x29, 0xe8, 0x96, 0x15, 0x4d, 0xe3, 0x0b, 0x0d, 0x4d, 0x26, 0xf7, 0x00, 0x07, 0x96, 0xd1, 0x61,
-	0x25, 0x67, 0x7e, 0x11, 0x8e, 0x0c, 0x90, 0xb4, 0x89, 0x30, 0x69, 0x2e, 0x5e, 0x8b, 0xe0, 0x1c,
-	0xe6, 0x38, 0xcf, 0xe7, 0xe2, 0x14, 0xfb, 0x47, 0x80, 0x3e, 0xd4, 0x94, 0x60, 0x88, 0x78, 0x1d,
-	0x74, 0x30, 0xe2, 0x85, 0x34, 0x9c, 0x38, 0x1a, 0xef, 0x69, 0xe8, 0x6c, 0x1c, 0x44, 0xa9, 0x03,
-	0xbe, 0xd7, 0x0f, 0x1a, 0x4e, 0xe4, 0xa8, 0x0d, 0xc7, 0x8e, 0x5a, 0x24, 0x71, 0x41, 0x3c, 0xc2,
-	0xc4, 0x29, 0x95, 0xd7, 0x37, 0x71, 0x4a, 0xe9, 0x4d, 0x84, 0xa5, 0x77, 0x80, 0x89, 0x9b, 0x45,
-	0x47, 0x39, 0xce, 0x8d, 0xd5, 0x2d, 0x19, 0xa0, 0x93, 0x68, 0xcc, 0x63, 0xf7, 0xa8, 0x1d, 0x9e,
-	0xa7, 0xbf, 0xf2, 0xf7, 0xf5, 0xba, 0xf1, 0x26, 0x9c, 0x72, 0x11, 0x53, 0xae, 0x13, 0x9c, 0xa6,
-	0xf1, 0x16, 0xf5, 0x48, 0xa5, 0x4e, 0x3c, 0x02, 0x41, 0x35, 0xb2, 0x2b, 0xf1, 0x16, 0xf5, 0xc8,
-	0x0d, 0xe2, 0x91, 0xf2, 0x58, 0x0b, 0x9e, 0x02, 0xd3, 0xc2, 0xe3, 0x17, 0x31, 0x2d, 0x34, 0x53,
-	0x4c, 0xbf, 0x81, 0x4e, 0x70, 0xd3, 0x6b, 0x0e, 0xdb, 0x6d, 0xab, 0x96, 0xaf, 0x27, 0x2d, 0x9f,
-	0x4d, 0xb3, 0xcc, 0x15, 0x53, 0x0c, 0xbf, 0xab, 0xa1, 0x29, 0x71, 0x47, 0xb0, 0x1d, 0xab, 0xd6,
-	0x59, 0x65, 0xce, 0x72, 0xad, 0xc6, 0x76, 0xed, 0xa0, 0xf7, 0xe9, 0x68, 0xcc, 0xa1, 0x2e, 0xdb,
-	0x75, 0x6a, 0xb2, 0xf1, 0x05, 0xef, 0x78, 0x05, 0xfd, 0xbd, 0xed, 0x58, 0x76, 0xcd, 0x6a, 0x93,
-	0x9d, 0x0a, 0xa9, 0xd7, 0x1d, 0xea, 0xba, 0xa2, 0x8e, 0x4a, 0x93, 0x3f, 0x7d, 0x3b, 0x77, 0x1c,
-	0x92, 0xb9, 0x2c, 0xbe, 0xbc, 0xee, 0x39, 0x96, 0xdd, 0x2c, 0x1f, 0x0b, 0x54, 0x60, 0xdd, 0xb8,
-	0x23, 0x6f, 0xb9, 0x04, 0x04, 0x70, 0x72, 0x11, 0x8d, 0xb6, 0xf9, 0x37, 0xf0, 0x70, 0x5a, 0xf5,
-	0x30, 0xbc, 0xe2, 0x8b, 0xc2, 0x40, 0x19, 0x84, 0x8d, 0x5f, 0xa5, 0x6f, 0x77, 0xa8, 0x63, 0x35,
-	0x3a, 0x9b, 0x81, 0xa0, 0xf4, 0xed, 0x0a, 0x1a, 0x63, 0x6d, 0xea, 0x10, 0x8f, 0x39, 0xc2, 0xb7,
-	0x3e, 0xb0, 0x03, 0xc9, 0xdc, 0x43, 0x1c, 0xbf, 0x0d, 0x46, 0xe2, 0xb7, 0x01, 0x2e, 0xa1, 0x09,
-	0x52, 0xf3, 0x6b, 0xb7, 0xe2, 0xcf, 0x14, 0x93, 0x87, 0xce, 0x68, 0x33, 0x47, 0xa2, 0x69, 0x53,
-	0x9c, 0x5a, 0xe6, 0x92, 0x5b, 0x9d, 0x36, 0x2d, 0x23, 0x12, 0x3c, 0x07, 0x41, 0x4b, 0xfa, 0x16,
-	0x06, 0x8d, 0x36, 0x1a, 0xb4, 0xe6, 0x71, 0xd7, 0x8e, 0x64, 0x06, 0x6d, 0x85, 0x0b, 0x95, 0x41,
-	0xd8, 0xb8, 0x0f, 0x95, 0xe6, 0x5f, 0x37, 0xbc, 0x68, 0x64, 0xb0, 0x96, 0xd0, 0x44, 0xd3, 0x7f,
-	0xaf, 0xb0, 0xb7, 0x6d, 0x9a, 0x1f, 0x2f, 0xc4, 0x85, 0x6f, 0xfb, 0xb2, 0x78, 0x1a, 0x89, 0x37,
-	0x35, 0x60, 0xe3, 0x7c, 0x85, 0x37, 0xbd, 0x3b, 0xca, 0xc5, 0x0b, 0x5b, 0x82, 0x0f, 0x2f, 0x49,
-	0x45, 0xe5, 0x7e, 0x9b, 0xce, 0x2c, 0x6f, 0xde, 0x63, 0x84, 0x5d, 0x7e, 0xbb, 0x7d, 0xa4, 0x81,
-	0x2f, 0x7e, 0x07, 0x8b, 0xf8, 0x72, 0x50, 0x0d, 0x34, 0x16, 0x93, 0xe1, 0xc1, 0x63, 0x62, 0x7c,
-	0xaa, 0x81, 0xd7, 0x0a, 0x38, 0xf0, 0x7a, 0x2d, 0x05, 0xdd, 0x8b, 0x74, 0x46, 0x7c, 0x5d, 0xc2,
-	0x13, 0x4d, 0x7a, 0x98, 0x37, 0xe9, 0x9c, 0xf8, 0xa1, 0x20, 0x7e, 0xae, 0xf1, 0x95, 0x86, 0x4e,
-	0x45, 0x33, 0x73, 0x8b, 0xb6, 0xaa, 0xd4, 0x91, 0x61, 0x9c, 0x47, 0xa3, 0x2d, 0xbe, 0x90, 0x5b,
-	0x0d, 0x20, 0xb7, 0x8f, 0x80, 0xc5, 0x8a, 0x68, 0x24, 0x5e, 0x44, 0x14, 0xce, 0x7a, 0x02, 0x2a,
-	0x04, 0x75, 0x05, 0x1d, 0x16, 0xea, 0x0a, 0xe2, 0x58, 0x17, 0x56, 0x0e, 0x85, 0x6a, 0x41, 0x20,
-	0x16, 0x2f, 0x46, 0x03, 0x06, 0xb9, 0xa0, 0x57, 0x45, 0xea, 0xaa, 0x5f, 0xb3, 0x9c, 0x45, 0x38,
-	0x6c, 0x96, 0x90, 0x16, 0x79, 0xeb, 0x86, 0x3d, 0x51, 0x24, 0xa2, 0x6e, 0x6c, 0x41, 0xe4, 0xe3,
-	0xfb, 0xec, 0xaf, 0x23, 0x2e, 0x42, 0xcd, 0x89, 0xe5, 0xd8, 0x08, 0x2a, 0x64, 0x94, 0x11, 0x54,
-	0x2c, 0xac, 0xd7, 0x8d, 0x4d, 0x98, 0x8c, 0x54, 0xb5, 0x7d, 0x01, 0xb9, 0xf4, 0xe8, 0x24, 0xfa,
-	0x0b, 0x37, 0x89, 0x7b, 0x68, 0x54, 0xf0, 0x0b, 0x7c, 0x2e, 0xad, 0x30, 0x93, 0x2c, 0x4b, 0x3f,
-	0x9f, 0x2b, 0x27, 0xb0, 0x19, 0xc6, 0xc3, 0x9f, 0xff, 0x78, 0x34, 0x3c, 0x85, 0x75, 0x33, 0x93,
-	0xee, 0xe1, 0x6f, 0xe4, 0x31, 0x4c, 0x70, 0x24, 0xbc, 0x90, 0xb3, 0x4f, 0x92, 0x8e, 0xe9, 0x97,
-	0xf6, 0xa2, 0x02, 0x28, 0x8b, 0x1c, 0xe5, 0x0c, 0x3e, 0x97, 0x8d, 0xd2, 0xec, 0x06, 0x9c, 0xae,
-	0x87, 0x3f, 0xd6, 0x10, 0x0a, 0xe9, 0x0f, 0xbe, 0x98, 0xb9, 0x65, 0x82, 0x99, 0xe9, 0xff, 0x1d,
-	0x48, 0x16, 0x70, 0x2d, 0x72, 0x5c, 0x26, 0x9e, 0x4b, 0xc3, 0x75, 0x97, 0x92, 0x7a, 0x45, 0x5c,
-	0x79, 0x66, 0x57, 0xb9, 0x0d, 0x7b, 0xf8, 0x4b, 0x0d, 0x1d, 0x89, 0x12, 0x3b, 0x5c, 0x1c, 0x60,
-	0x5b, 0xa5, 0x16, 0xf7, 0x06, 0x73, 0x89, 0xc3, 0xbc, 0x8c, 0x17, 0x72, 0x60, 0x56, 0xaa, 0x7e,
-	0x69, 0x07, 0x60, 0xad, 0x7a, 0x0f, 0x3f, 0xd6, 0xd0, 0xdf, 0x42, 0x8b, 0x1b, 0xab, 0x5b, 0xf8,
-	0xdf, 0x99, 0x3b, 0x87, 0xc3, 0xa5, 0x9e, 0x1d, 0xf1, 0xc4, 0x4c, 0x69, 0xfc, 0x8f, 0xa3, 0x9b,
-	0xc7, 0xc5, 0x3c, 0x74, 0x76, 0xc3, 0x33, 0xbb, 0x72, 0x66, 0xed, 0xe1, 0xaf, 0x21, 0xc9, 0x62,
-	0x20, 0xcc, 0x49, 0x72, 0x84, 0xac, 0xe6, 0x44, 0x2f, 0xca, 0x20, 0x8d, 0x57, 0x38, 0xbe, 0x6b,
-	0xf8, 0x6a, 0x26, 0x3e, 0x31, 0xb6, 0x44, 0x93, 0x6c, 0x76, 0x95, 0xf9, 0x26, 0x4c, 0x79, 0x48,
-	0x6c, 0x73, 0x52, 0x9e, 0x60, 0xc0, 0x7b, 0x03, 0x9d, 0x9f, 0x72, 0x80, 0x07, 0x29, 0x0f, 0xb8,
-	0x75, 0x98, 0xf2, 0x60, 0x44, 0xdf, 0x6f, 0xca, 0x13, 0xb3, 0xfe, 0x00, 0x29, 0x97, 0xc1, 0x8b,
-	0xa6, 0xfc, 0x03, 0x0d, 0x4d, 0x28, 0x1c, 0x19, 0x67, 0x87, 0x24, 0xc9, 0xd6, 0xf5, 0xd9, 0xc1,
-	0x84, 0x01, 0xe2, 0x0c, 0x87, 0x68, 0xe0, 0x33, 0x69, 0x10, 0x77, 0x2c, 0xd7, 0x83, 0xaa, 0x74,
-	0xf1, 0x27, 0x00, 0x0a, 0xf8, 0x5f, 0x0e, 0xa8, 0x28, 0x6b, 0xce, 0x01, 0x15, 0xa3, 0x94, 0xfd,
-	0xe3, 0xc6, 0x41, 0x89, 0xb8, 0xb9, 0xb1, 0x86, 0xf3, 0x9d, 0x86, 0x4e, 0xa4, 0xb2, 0x65, 0xbc,
-	0x38, 0xc8, 0xfe, 0x09, 0x76, 0xbd, 0x47, 0xd8, 0xcb, 0x1c, 0xf6, 0x55, 0xbc, 0x94, 0x07, 0xdb,
-	0xaf, 0xc6, 0xa0, 0xf9, 0x44, 0xfa, 0xd0, 0x87, 0x1a, 0x3a, 0x1c, 0x8c, 0x2d, 0x03, 0xd7, 0xe4,
-	0x85, 0x4c, 0xa1, 0x38, 0x49, 0x1c, 0xa0, 0x95, 0xc3, 0x64, 0x15, 0xad, 0xc8, 0x1f, 0xe4, 0xfc,
-	0x1c, 0x27, 0x66, 0x78, 0x3e, 0xfb, 0x9e, 0x4b, 0xa7, 0x91, 0xfa, 0xc2, 0x1e, 0x34, 0x00, 0xf5,
-	0x2d, 0x8e, 0x7a, 0x0d, 0xaf, 0xa4, 0x5e, 0x8c, 0x62, 0x58, 0x69, 0x30, 0xa7, 0x42, 0x84, 0x9e,
-	0xd9, 0x95, 0xa3, 0x56, 0xcf, 0xec, 0x26, 0x68, 0x69, 0x0f, 0xff, 0xa8, 0xa1, 0x63, 0x71, 0xb2,
-	0xd4, 0xc7, 0x91, 0x0c, 0xce, 0xd8, 0xc7, 0x91, 0x2c, 0x26, 0x66, 0x6c, 0x71, 0x47, 0x36, 0xf0,
-	0xcd, 0x34, 0x47, 0x1e, 0x70, 0xad, 0x8a, 0xf2, 0x43, 0x75, 0x57, 0x32, 0xcd, 0x5e, 0xbc, 0xeb,
-	0x2a, 0xa4, 0xb1, 0x87, 0x3f, 0xd7, 0xd0, 0x78, 0x50, 0x35, 0xf8, 0x42, 0xdf, 0x06, 0xaa, 0x0e,
-	0xa9, 0xfa, 0xc5, 0x41, 0x44, 0x07, 0xa9, 0xee, 0xb0, 0x72, 0xcc, 0xae, 0x32, 0xd5, 0xf7, 0xe4,
-	0x9b, 0x38, 0x9f, 0x8f, 0x35, 0x34, 0x1e, 0x70, 0x9c, 0x3e, 0x38, 0xe3, 0x24, 0xad, 0x0f, 0xce,
-	0x04, 0x65, 0x32, 0xae, 0x70, 0x9c, 0x45, 0x3c, 0x9b, 0x79, 0x0a, 0x53, 0x70, 0xe2, 0xcf, 0x34,
-	0x74, 0x34, 0xc6, 0x17, 0xb0, 0x99, 0x1f, 0x9d, 0x08, 0x09, 0xd2, 0xe7, 0x07, 0x57, 0x00, 0xb0,
-	0x73, 0x1c, 0xec, 0x79, 0xfc, 0x9f, 0x9c, 0xe3, 0x08, 0x9c, 0xe9, 0x7b, 0x4d, 0xfe, 0xad, 0x10,
-	0xe1, 0x02, 0x7d, 0xee, 0xd8, 0x54, 0x72, 0xa2, 0x9b, 0x03, 0xcb, 0x03, 0xce, 0x9b, 0x1c, 0xe7,
-	0x2a, 0xbe, 0x91, 0x73, 0x00, 0x21, 0xb4, 0xa9, 0xc7, 0x4f, 0x12, 0x9d, 0x9e, 0x7f, 0x95, 0x1c,
-	0x8d, 0xb1, 0x88, 0x3e, 0x73, 0x4d, 0x82, 0xa1, 0xf4, 0x19, 0x11, 0x92, 0xb4, 0xa4, 0x7f, 0x3d,
-	0x00, 0x74, 0x98, 0x0e, 0x02, 0xda, 0xd3, 0x2b, 0xad, 0x3f, 0x79, 0x56, 0xd0, 0x9e, 0x3e, 0x2b,
-	0x68, 0xbf, 0x3f, 0x2b, 0x68, 0xef, 0x3f, 0x2f, 0x0c, 0x3d, 0x7d, 0x5e, 0x18, 0xfa, 0xe5, 0x79,
-	0x61, 0x68, 0xdb, 0x6c, 0x5a, 0xde, 0xdd, 0xdd, 0x6a, 0xb1, 0xc6, 0x5a, 0x66, 0xd5, 0xae, 0xce,
-	0xd5, 0xee, 0x12, 0xcb, 0x56, 0x6d, 0xbf, 0x13, 0xfd, 0x9b, 0xa8, 0x3a, 0xca, 0xff, 0x27, 0xba,
-	0xfc, 0x67, 0x00, 0x00, 0x00, 0xff, 0xff, 0x16, 0xb9, 0xc4, 0x84, 0x60, 0x1b, 0x00, 0x00,
+	// 1956 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x59, 0xcd, 0x8f, 0x1c, 0x47,
+	0x15, 0x77, 0xef, 0x9a, 0x65, 0xb7, 0x66, 0xb1, 0x43, 0xb1, 0x01, 0xa7, 0xb3, 0x1e, 0xdb, 0x1d,
+	0xb0, 0x37, 0x8e, 0x77, 0xda, 0xeb, 0xc4, 0x88, 0x55, 0x3e, 0xd0, 0x0e, 0xf1, 0x2e, 0x23, 0xf9,
+	0x8b, 0xf1, 0xca, 0x88, 0x95, 0x50, 0xab, 0xa6, 0xbb, 0x66, 0xdc, 0xec, 0x74, 0x57, 0xbb, 0xbb,
+	0xc7, 0x30, 0x19, 0xcd, 0x81, 0x5c, 0xe0, 0x88, 0x40, 0x48, 0x11, 0x22, 0x12, 0x08, 0x81, 0x80,
+	0x0b, 0x97, 0x5c, 0x38, 0x71, 0xe1, 0x10, 0x09, 0x21, 0x45, 0xe1, 0x82, 0x72, 0x88, 0x90, 0xcd,
+	0x1f, 0x12, 0x75, 0xd5, 0xeb, 0xee, 0xea, 0xaf, 0xe9, 0xd9, 0xec, 0x9e, 0x76, 0xba, 0xfa, 0xbd,
+	0x57, 0xbf, 0xf7, 0x51, 0xaf, 0xde, 0xaf, 0x17, 0x35, 0x07, 0x3e, 0xa5, 0x6e, 0xdf, 0xa6, 0x43,
+	0x4b, 0x0f, 0x42, 0xe6, 0x93, 0x01, 0xd5, 0x1f, 0x8f, 0xa8, 0x3f, 0x6e, 0x79, 0x3e, 0x0b, 0x19,
+	0xc6, 0xe9, 0xfb, 0x16, 0xbc, 0x57, 0xaf, 0x9a, 0x2c, 0x70, 0x58, 0xa0, 0xf7, 0x48, 0x00, 0xc2,
+	0xfa, 0x93, 0xad, 0x1e, 0x0d, 0xc9, 0x96, 0xee, 0x91, 0x81, 0xed, 0x92, 0xd0, 0x66, 0xae, 0xd0,
+	0x57, 0x5f, 0x10, 0xb2, 0x06, 0x7f, 0xd2, 0xc5, 0x03, 0xbc, 0x5a, 0x1b, 0xb0, 0x01, 0x13, 0xeb,
+	0xd1, 0x2f, 0x58, 0x5d, 0x1f, 0x30, 0x36, 0x18, 0x52, 0x9d, 0x78, 0xb6, 0x4e, 0x5c, 0x97, 0x85,
+	0xdc, 0x5a, 0xac, 0xa3, 0x49, 0x70, 0x3d, 0xea, 0x3b, 0x76, 0x10, 0xd8, 0xcc, 0xd5, 0x4d, 0xe6,
+	0x38, 0xc9, 0x96, 0x97, 0xca, 0x65, 0xc2, 0xb1, 0x47, 0x63, 0x33, 0x17, 0x4a, 0xbc, 0xf6, 0x88,
+	0x4f, 0x9c, 0x58, 0xa0, 0x2c, 0x2c, 0xb2, 0x81, 0x97, 0xa4, 0xf7, 0x4f, 0x6c, 0x3f, 0x1c, 0x91,
+	0xe1, 0xc0, 0x67, 0x23, 0x4f, 0x16, 0xd2, 0xd6, 0x10, 0xfe, 0x5e, 0x14, 0x9d, 0xfb, 0xdc, 0x72,
+	0x97, 0x3e, 0x1e, 0xd1, 0x20, 0xd4, 0xee, 0xa1, 0xaf, 0x64, 0x56, 0x03, 0x8f, 0xb9, 0x01, 0xc5,
+	0xdf, 0x42, 0x4b, 0x02, 0xc1, 0x39, 0xe5, 0xa2, 0xb2, 0xd1, 0xb8, 0xa1, 0xb6, 0x8a, 0x91, 0x6f,
+	0x09, 0x9d, 0xf6, 0xe9, 0x0f, 0x3f, 0xbd, 0x70, 0xaa, 0x0b, 0xf2, 0xda, 0x9b, 0xe8, 0xbc, 0x64,
+	0xb0, 0x3d, 0xde, 0xb7, 0x1d, 0x1a, 0x84, 0xc4, 0xf1, 0x60, 0x47, 0xbc, 0x8e, 0x56, 0xc2, 0x78,
+	0x8d, 0x5b, 0x5f, 0xec, 0xa6, 0x0b, 0xda, 0x01, 0x6a, 0x56, 0xa9, 0x1f, 0x1b, 0xda, 0x36, 0xfa,
+	0x2a, 0xb7, 0xfd, 0x5d, 0x4a, 0xac, 0xf6, 0xc8, 0x3c, 0xa4, 0x61, 0x8c, 0xe9, 0x02, 0x6a, 0xf4,
+	0xf8, 0x82, 0xe1, 0x12, 0x87, 0x72, 0xc3, 0x2b, 0x5d, 0x24, 0x96, 0xee, 0x12, 0x87, 0x6a, 0xdb,
+	0x48, 0xcd, 0xa9, 0xb6, 0xc7, 0x1d, 0x2b, 0x56, 0x7f, 0x11, 0xad, 0x80, 0xba, 0x6d, 0x81, 0xf2,
+	0xb2, 0x58, 0xe8, 0x58, 0xda, 0x01, 0xfa, 0x5a, 0x61, 0x57, 0x70, 0xe5, 0xdb, 0xc9, 0xb6, 0xb6,
+	0xdb, 0x67, 0xe0, 0x4f, 0xb3, 0xcc, 0x1f, 0xa1, 0xd8, 0x71, 0xfb, 0x2c, 0x86, 0x15, 0xfd, 0xd6,
+	0x0e, 0x24, 0x8f, 0xee, 0xf5, 0x7e, 0x44, 0xcd, 0xb9, 0x3d, 0x8a, 0x04, 0x18, 0xd7, 0x10, 0x02,
+	0x0b, 0x42, 0x40, 0x2c, 0x15, 0x5c, 0x16, 0xb6, 0x73, 0x2e, 0x83, 0x7a, 0xea, 0xb2, 0x58, 0xe8,
+	0x58, 0xda, 0xdf, 0x15, 0xc9, 0xe7, 0x18, 0x57, 0xea, 0x73, 0xac, 0x58, 0xe3, 0xb3, 0x50, 0x14,
+	0x3e, 0xb3, 0xe4, 0x37, 0xfe, 0x21, 0x5a, 0x1b, 0x0c, 0x59, 0x8f, 0x0c, 0x0d, 0x28, 0x75, 0x83,
+	0xd7, 0x3a, 0xf7, 0xa0, 0x71, 0xe3, 0x15, 0xd9, 0x92, 0x7c, 0x16, 0x5a, 0x7b, 0x5c, 0xe9, 0xa1,
+	0x58, 0xda, 0x8b, 0x96, 0xba, 0x78, 0x50, 0x58, 0xd3, 0x08, 0x40, 0xbf, 0x6d, 0x07, 0xa1, 0x88,
+	0x7a, 0x7c, 0x56, 0xf0, 0x2e, 0x42, 0x69, 0x47, 0x01, 0xe4, 0x97, 0x5b, 0xd0, 0x45, 0xa2, 0xf6,
+	0xd3, 0x12, 0xbd, 0x0a, 0xda, 0x4f, 0xeb, 0x3e, 0x19, 0x50, 0xd0, 0xed, 0x4a, 0x9a, 0xda, 0x9f,
+	0x14, 0x74, 0xae, 0xb8, 0x07, 0xc4, 0x67, 0x07, 0xad, 0x4a, 0x35, 0x11, 0x15, 0xf9, 0xe2, 0x1c,
+	0x45, 0xd1, 0x48, 0x8b, 0x22, 0xc0, 0x7b, 0x19, 0x9c, 0x22, 0x2e, 0x57, 0x6a, 0x71, 0x8a, 0xfd,
+	0x33, 0x40, 0xdf, 0x55, 0xa4, 0x60, 0x88, 0x74, 0x9c, 0x74, 0x30, 0xf2, 0x85, 0xba, 0x50, 0x38,
+	0x7a, 0x3f, 0x57, 0xd0, 0xa5, 0x3c, 0x88, 0xf6, 0x18, 0x7c, 0xb7, 0x4e, 0x1a, 0x4e, 0xe6, 0x28,
+	0x2f, 0xe4, 0x8e, 0x72, 0x26, 0x71, 0x49, 0x3c, 0xd2, 0xc4, 0x49, 0x85, 0x3d, 0x33, 0x71, 0x52,
+	0x65, 0x37, 0xd2, 0xca, 0x3e, 0xc1, 0xc4, 0x5d, 0x43, 0x67, 0x39, 0xce, 0xbb, 0xbb, 0xfb, 0x71,
+	0x80, 0x5e, 0x40, 0xcb, 0x21, 0x3b, 0xa4, 0x6e, 0x7a, 0x5e, 0xbf, 0xc8, 0x9f, 0x3b, 0x96, 0xf6,
+	0x03, 0xe8, 0x22, 0x22, 0xa6, 0x5c, 0x27, 0x39, 0xac, 0x2b, 0x0e, 0x0d, 0x89, 0x61, 0x91, 0x90,
+	0x40, 0x50, 0xb5, 0xea, 0x4a, 0xbc, 0x43, 0x43, 0xf2, 0x36, 0x09, 0x49, 0x77, 0xd9, 0x81, 0x5f,
+	0x89, 0x69, 0xe1, 0xf1, 0xe7, 0x31, 0x2d, 0x34, 0x4b, 0x4c, 0x7f, 0x1f, 0x3d, 0xcf, 0x4d, 0xf3,
+	0x63, 0x2b, 0x5b, 0x7e, 0xab, 0x68, 0xf9, 0x52, 0x99, 0x65, 0xae, 0x58, 0x62, 0xf8, 0xa7, 0x0a,
+	0x5a, 0x17, 0x77, 0x10, 0x1b, 0xda, 0xe6, 0x78, 0x97, 0xf9, 0x3b, 0xa6, 0xc9, 0x46, 0x6e, 0xd2,
+	0x5b, 0x55, 0xb4, 0xec, 0xd3, 0x80, 0x8d, 0x7c, 0x33, 0x6e, 0xac, 0xc9, 0x33, 0xbe, 0x85, 0xbe,
+	0xec, 0xf9, 0xb6, 0x6b, 0xda, 0x1e, 0x19, 0x1a, 0xc4, 0xb2, 0x7c, 0x1a, 0x04, 0xa2, 0x8e, 0xda,
+	0xe7, 0x3e, 0xfe, 0x60, 0x73, 0x0d, 0x92, 0xb9, 0x23, 0xde, 0x3c, 0x08, 0x7d, 0xdb, 0x1d, 0x74,
+	0x9f, 0x4b, 0x54, 0x60, 0x5d, 0x7b, 0x18, 0xdf, 0xa2, 0x05, 0x08, 0xe0, 0xe4, 0x4d, 0xb4, 0xe4,
+	0xf1, 0x77, 0xe0, 0xe1, 0x79, 0xd9, 0xc3, 0x74, 0xce, 0x68, 0x09, 0x03, 0x5d, 0x10, 0xd6, 0x3e,
+	0x89, 0x7d, 0x7b, 0x48, 0x7d, 0xbb, 0x3f, 0xbe, 0x9f, 0x08, 0xc6, 0xbe, 0xbd, 0x86, 0x96, 0x99,
+	0x47, 0x7d, 0x12, 0x32, 0x5f, 0xf8, 0x36, 0x03, 0x76, 0x22, 0x59, 0x7b, 0x88, 0xf3, 0xb7, 0xcd,
+	0x62, 0xfe, 0xb6, 0xc1, 0x6d, 0xd4, 0x20, 0x66, 0x54, 0xbb, 0x46, 0x34, 0xb3, 0x9c, 0x3b, 0x7d,
+	0x51, 0xd9, 0x38, 0x93, 0x4d, 0x9b, 0xe4, 0xd4, 0x0e, 0x97, 0xdc, 0x1f, 0x7b, 0xb4, 0x8b, 0x48,
+	0xf2, 0x3b, 0x09, 0x5a, 0xd1, 0xb7, 0x34, 0x68, 0xb4, 0xdf, 0xa7, 0x66, 0xc8, 0x5d, 0x3b, 0x53,
+	0x19, 0xb4, 0x5b, 0x5c, 0xa8, 0x0b, 0xc2, 0xda, 0x63, 0xa8, 0xb4, 0xe8, 0x36, 0x13, 0x17, 0x07,
+	0x04, 0x6b, 0x1b, 0x35, 0xf8, 0xdd, 0x62, 0xb0, 0x1f, 0xbb, 0xb4, 0x3e, 0x5e, 0x88, 0x0b, 0xdf,
+	0x8b, 0x64, 0xf1, 0x79, 0x24, 0x9e, 0xe4, 0x80, 0xad, 0xf0, 0x15, 0xde, 0xf4, 0x1e, 0x4a, 0x17,
+	0x3b, 0x6c, 0x09, 0x3e, 0xbc, 0x11, 0x2b, 0x4a, 0xd7, 0xe7, 0xf9, 0xca, 0xf2, 0xe6, 0x3d, 0x46,
+	0xd8, 0xe5, 0x03, 0xc3, 0x6f, 0x14, 0xf0, 0x25, 0xea, 0x60, 0x19, 0x5f, 0x4e, 0xaa, 0x81, 0xe6,
+	0x62, 0xb2, 0x30, 0x7f, 0x4c, 0xb4, 0xdf, 0x2b, 0xe0, 0xb5, 0x04, 0x0e, 0xbc, 0xde, 0x2b, 0x41,
+	0xf7, 0x79, 0x3a, 0x23, 0x7e, 0x2b, 0x86, 0x27, 0x9a, 0xf4, 0x02, 0x6f, 0xd2, 0x35, 0xf1, 0x43,
+	0x49, 0xfc, 0x02, 0xed, 0x2f, 0x0a, 0x7a, 0x31, 0x9b, 0x99, 0x3b, 0xd4, 0xe9, 0x51, 0x3f, 0x0e,
+	0xe3, 0x75, 0xb4, 0xe4, 0xf0, 0x85, 0xda, 0x6a, 0x00, 0xb9, 0x63, 0x04, 0x2c, 0x57, 0x44, 0x8b,
+	0xf9, 0x22, 0xa2, 0x70, 0xd6, 0x0b, 0x50, 0x21, 0xa8, 0xb7, 0xd0, 0xaa, 0x50, 0x97, 0x10, 0xe7,
+	0xba, 0xb0, 0x74, 0x28, 0x64, 0x0b, 0x02, 0xb1, 0x78, 0xd0, 0xfa, 0x30, 0x28, 0x26, 0xbd, 0x2a,
+	0x53, 0x57, 0xb3, 0x9a, 0xe5, 0x35, 0x84, 0xd3, 0x66, 0x09, 0x69, 0x89, 0x6f, 0xdd, 0xb4, 0x27,
+	0x8a, 0x44, 0x58, 0xda, 0x3e, 0x44, 0x3e, 0xbf, 0xcf, 0xf1, 0x3a, 0xe2, 0x4d, 0xa8, 0x39, 0xb1,
+	0x9c, 0x1b, 0x71, 0x85, 0x8c, 0x34, 0xe2, 0x8a, 0x85, 0x8e, 0xa5, 0xdd, 0x87, 0xc9, 0x48, 0x56,
+	0x3b, 0x1e, 0x90, 0xf7, 0x15, 0xa0, 0x62, 0xb7, 0x99, 0x79, 0xb8, 0x4b, 0x69, 0x7a, 0x30, 0xa3,
+	0x20, 0x39, 0xc4, 0x1f, 0x1b, 0x81, 0x97, 0x5c, 0x29, 0xca, 0x1c, 0x57, 0x4a, 0xa4, 0xf3, 0xc0,
+	0x83, 0xf5, 0xc8, 0x1d, 0xd3, 0xa7, 0x24, 0xa4, 0x06, 0x09, 0x79, 0x8c, 0x17, 0xbb, 0xcb, 0x62,
+	0x61, 0x27, 0xc4, 0x97, 0xd0, 0xaa, 0x47, 0xc6, 0x43, 0x46, 0x2c, 0x23, 0xb0, 0xdf, 0x11, 0xb5,
+	0x74, 0xba, 0xdb, 0x80, 0xb5, 0x07, 0xf6, 0x3b, 0x54, 0x1b, 0xa2, 0xb5, 0x2c, 0x3c, 0x70, 0x77,
+	0x1f, 0x2d, 0x11, 0x27, 0xba, 0x9b, 0x00, 0xd3, 0x1b, 0x11, 0xe7, 0xfa, 0xe4, 0xd3, 0x0b, 0x97,
+	0x07, 0x76, 0xf8, 0x68, 0xd4, 0x6b, 0x99, 0xcc, 0x01, 0xa6, 0x0d, 0x7f, 0x36, 0x03, 0xeb, 0x10,
+	0x98, 0x69, 0xc7, 0x0d, 0x3f, 0xfe, 0x60, 0x13, 0x81, 0x07, 0x1d, 0x37, 0xec, 0x82, 0xad, 0x1b,
+	0xef, 0xab, 0xe8, 0x0b, 0x7c, 0x3b, 0x3c, 0x45, 0x4b, 0x82, 0xcd, 0xe1, 0xcb, 0x65, 0xc7, 0xb4,
+	0xc8, 0x69, 0xd5, 0x2b, 0xb5, 0x72, 0x02, 0xba, 0xa6, 0xbd, 0xfb, 0x9f, 0xff, 0xff, 0x6a, 0x61,
+	0x1d, 0xab, 0x7a, 0x25, 0x03, 0xc7, 0x7f, 0x8b, 0x9b, 0x52, 0x81, 0x91, 0xe2, 0xad, 0x9a, 0x7d,
+	0x8a, 0xe4, 0x57, 0xbd, 0x71, 0x14, 0x15, 0x40, 0xd9, 0xe2, 0x28, 0x37, 0xf0, 0xe5, 0x6a, 0x94,
+	0xfa, 0x24, 0x61, 0xd0, 0x53, 0xfc, 0x5b, 0x05, 0xa1, 0x94, 0x6c, 0xe2, 0xab, 0x95, 0x5b, 0x16,
+	0x78, 0xb0, 0xfa, 0xca, 0x5c, 0xb2, 0x80, 0xeb, 0x26, 0xc7, 0xa5, 0xe3, 0xcd, 0x32, 0x5c, 0x8f,
+	0x28, 0xb1, 0x0c, 0x31, 0x00, 0xe8, 0x13, 0x69, 0x36, 0x98, 0xe2, 0x3f, 0x2b, 0xe8, 0x4c, 0x96,
+	0x46, 0xe3, 0xd6, 0x1c, 0xdb, 0x4a, 0x27, 0xf3, 0x68, 0x30, 0xb7, 0x39, 0xcc, 0x57, 0xf1, 0x56,
+	0x0d, 0x4c, 0xa3, 0x17, 0x1d, 0xf4, 0x04, 0xac, 0x6d, 0x4d, 0xf1, 0x7b, 0x0a, 0xfa, 0x52, 0x6a,
+	0xf1, 0xee, 0xee, 0x3e, 0x7e, 0xa9, 0x72, 0xe7, 0x74, 0xd4, 0x56, 0xab, 0x23, 0x5e, 0x98, 0xb0,
+	0xb5, 0x6f, 0x72, 0x74, 0xd7, 0x71, 0xab, 0x0e, 0x9d, 0xdb, 0x0f, 0xf5, 0x49, 0x3c, 0xc1, 0x4f,
+	0xf1, 0x5f, 0x21, 0xc9, 0x62, 0x3c, 0xae, 0x49, 0x72, 0xe6, 0xd3, 0x40, 0x4d, 0xf4, 0xb2, 0x74,
+	0x5d, 0xfb, 0x0e, 0xc7, 0xf7, 0x26, 0x7e, 0xbd, 0x12, 0x9f, 0x18, 0xe2, 0xb2, 0x49, 0xd6, 0x27,
+	0xd2, 0xb4, 0x97, 0xa6, 0x3c, 0xfd, 0x8c, 0x50, 0x93, 0xf2, 0xc2, 0xf7, 0x86, 0xa3, 0x81, 0xae,
+	0x4f, 0x39, 0xc0, 0x83, 0x94, 0x27, 0x5f, 0x32, 0xd2, 0x94, 0x27, 0x84, 0xe5, 0xb8, 0x29, 0x2f,
+	0x30, 0x9f, 0x39, 0x52, 0x1e, 0x07, 0x2f, 0x9b, 0xf2, 0x5f, 0x2a, 0xa8, 0x21, 0x7d, 0x31, 0xc0,
+	0xd5, 0x21, 0x29, 0x7e, 0xbb, 0x50, 0xaf, 0xcd, 0x27, 0x0c, 0x10, 0x37, 0x38, 0x44, 0x0d, 0x5f,
+	0x2c, 0x83, 0x38, 0xb4, 0x83, 0x10, 0xaa, 0x32, 0xc0, 0xbf, 0x03, 0x50, 0xc0, 0x86, 0x6b, 0x40,
+	0x65, 0xbf, 0x21, 0xd4, 0x80, 0xca, 0x11, 0xec, 0xd9, 0x71, 0xe3, 0xa0, 0x44, 0xdc, 0x82, 0x5c,
+	0xc3, 0xf9, 0x87, 0x82, 0x9e, 0x2f, 0xfd, 0x76, 0x80, 0x6f, 0xce, 0xb3, 0x7f, 0xe1, 0x5b, 0xc3,
+	0x11, 0x61, 0xef, 0x70, 0xd8, 0xaf, 0xe3, 0xed, 0x3a, 0xd8, 0x51, 0x35, 0x26, 0xcd, 0x27, 0xd3,
+	0x87, 0x7e, 0xad, 0xa0, 0xd5, 0x64, 0x88, 0x9b, 0xbb, 0x26, 0x5f, 0xae, 0x14, 0xca, 0x53, 0xe6,
+	0x39, 0x5a, 0x39, 0xcc, 0x99, 0xd9, 0x8a, 0xfc, 0x57, 0xcc, 0x26, 0xf2, 0x34, 0x15, 0x5f, 0xaf,
+	0xbe, 0xe7, 0xca, 0x49, 0xb5, 0xba, 0x75, 0x04, 0x0d, 0x40, 0x7d, 0x87, 0xa3, 0xde, 0xc3, 0xb7,
+	0x4a, 0x2f, 0x46, 0x31, 0xba, 0xf5, 0x99, 0x6f, 0x10, 0xa1, 0xa7, 0x4f, 0xe2, 0xc1, 0x73, 0xaa,
+	0x4f, 0x0a, 0x24, 0x7d, 0x8a, 0xff, 0xad, 0xa0, 0xe7, 0xf2, 0xd4, 0x71, 0x86, 0x23, 0x15, 0x0c,
+	0x7a, 0x86, 0x23, 0x55, 0xbc, 0x54, 0xdb, 0xe7, 0x8e, 0xdc, 0xc5, 0xb7, 0xcb, 0x1c, 0x79, 0xc2,
+	0xb5, 0x0c, 0xe9, 0x7f, 0x07, 0x93, 0x98, 0x77, 0x4f, 0xf3, 0x5d, 0x57, 0xa2, 0xd0, 0x53, 0xfc,
+	0x47, 0x05, 0xad, 0x24, 0x55, 0x83, 0x5f, 0x9e, 0xd9, 0x40, 0xe5, 0x91, 0x5d, 0xbd, 0x3a, 0x8f,
+	0xe8, 0x3c, 0xd5, 0x9d, 0x56, 0x8e, 0x3e, 0x91, 0x38, 0xce, 0x34, 0x7e, 0x12, 0xe7, 0xf3, 0x3d,
+	0x05, 0xad, 0x24, 0x8c, 0x6f, 0x06, 0xce, 0x3c, 0x65, 0x9d, 0x81, 0xb3, 0x40, 0x20, 0xb5, 0xd7,
+	0x38, 0xce, 0x16, 0xbe, 0x56, 0x79, 0x0a, 0x4b, 0x70, 0xe2, 0x3f, 0x28, 0xe8, 0x6c, 0x8e, 0x3d,
+	0x61, 0xbd, 0x3e, 0x3a, 0x19, 0x4a, 0xa8, 0x5e, 0x9f, 0x5f, 0x01, 0xc0, 0x6e, 0x72, 0xb0, 0x57,
+	0xf0, 0x37, 0x6a, 0x8e, 0x23, 0x30, 0xc8, 0x7f, 0xc6, 0xcc, 0x21, 0xcb, 0x8c, 0x66, 0xdc, 0xb1,
+	0xa5, 0x54, 0x4d, 0xd5, 0xe7, 0x96, 0x07, 0x9c, 0xb7, 0x39, 0xce, 0x5d, 0xfc, 0x76, 0xcd, 0x01,
+	0x84, 0xd0, 0x96, 0x1e, 0xbf, 0x98, 0xf6, 0x4d, 0xa3, 0xab, 0xe4, 0x6c, 0x8e, 0x53, 0xcd, 0x98,
+	0x6b, 0x0a, 0x7c, 0x6d, 0xc6, 0x88, 0x50, 0x24, 0x69, 0xb3, 0xeb, 0x01, 0xa0, 0xc3, 0x74, 0x90,
+	0x90, 0xc0, 0x29, 0xfe, 0x99, 0x82, 0x56, 0x65, 0x12, 0x84, 0xab, 0xa9, 0x46, 0x96, 0xc5, 0xa9,
+	0x1b, 0xf5, 0x82, 0x80, 0xec, 0xeb, 0x1c, 0x59, 0x13, 0xaf, 0x97, 0x56, 0x2a, 0x33, 0x0f, 0x8d,
+	0x3e, 0xa5, 0xed, 0xce, 0x87, 0x4f, 0x9b, 0xca, 0x47, 0x4f, 0x9b, 0xca, 0xff, 0x9e, 0x36, 0x95,
+	0x5f, 0x3c, 0x6b, 0x9e, 0xfa, 0xe8, 0x59, 0xf3, 0xd4, 0x7f, 0x9f, 0x35, 0x4f, 0x1d, 0xe8, 0x12,
+	0xef, 0xea, 0xb9, 0xbd, 0x4d, 0xf3, 0x11, 0xb1, 0x5d, 0xd9, 0xd6, 0x4f, 0xb2, 0xff, 0x43, 0xec,
+	0x2d, 0xf1, 0xff, 0x0f, 0xbe, 0xfa, 0x59, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x18, 0x28, 0xf4,
+	0x7d, 0x1d, 0x00, 0x00,
 }
 
 // Reference imports to suppress errors if they are not otherwise used.
@@ -1804,6 +1930,8 @@ type QueryClient interface {
 	QueryPolicyForGroup(ctx context.Context, in *QueryPolicyForGroupRequest, opts ...grpc.CallOption) (*QueryPolicyForGroupResponse, error)
 	// Queries a policy by policy id
 	QueryPolicyById(ctx context.Context, in *QueryPolicyByIdRequest, opts ...grpc.CallOption) (*QueryPolicyByIdResponse, error)
+	// Queries lock fee for storing an object
+	QueryLockFee(ctx context.Context, in *QueryLockFeeRequest, opts ...grpc.CallOption) (*QueryLockFeeResponse, error)
 }
 
 type queryClient struct {
@@ -1985,6 +2113,15 @@ func (c *queryClient) QueryPolicyById(ctx context.Context, in *QueryPolicyByIdRe
 	return out, nil
 }
 
+func (c *queryClient) QueryLockFee(ctx context.Context, in *QueryLockFeeRequest, opts ...grpc.CallOption) (*QueryLockFeeResponse, error) {
+	out := new(QueryLockFeeResponse)
+	err := c.cc.Invoke(ctx, "/greenfield.storage.Query/QueryLockFee", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
 // QueryServer is the server API for Query service.
 type QueryServer interface {
 	// Parameters queries the parameters of the module.
@@ -2025,6 +2162,8 @@ type QueryServer interface {
 	QueryPolicyForGroup(context.Context, *QueryPolicyForGroupRequest) (*QueryPolicyForGroupResponse, error)
 	// Queries a policy by policy id
 	QueryPolicyById(context.Context, *QueryPolicyByIdRequest) (*QueryPolicyByIdResponse, error)
+	// Queries lock fee for storing an object
+	QueryLockFee(context.Context, *QueryLockFeeRequest) (*QueryLockFeeResponse, error)
 }
 
 // UnimplementedQueryServer can be embedded to have forward compatible implementations.
@@ -2088,6 +2227,9 @@ func (*UnimplementedQueryServer) QueryPolicyForGroup(ctx context.Context, req *Q
 func (*UnimplementedQueryServer) QueryPolicyById(ctx context.Context, req *QueryPolicyByIdRequest) (*QueryPolicyByIdResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method QueryPolicyById not implemented")
 }
+func (*UnimplementedQueryServer) QueryLockFee(ctx context.Context, req *QueryLockFeeRequest) (*QueryLockFeeResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method QueryLockFee not implemented")
+}
 
 func RegisterQueryServer(s grpc1.Server, srv QueryServer) {
 	s.RegisterService(&_Query_serviceDesc, srv)
@@ -2435,6 +2577,24 @@ func _Query_QueryPolicyById_Handler(srv interface{}, ctx context.Context, dec fu
 	return interceptor(ctx, in, info, handler)
 }
 
+func _Query_QueryLockFee_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(QueryLockFeeRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(QueryServer).QueryLockFee(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/greenfield.storage.Query/QueryLockFee",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(QueryServer).QueryLockFee(ctx, req.(*QueryLockFeeRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
 var _Query_serviceDesc = grpc.ServiceDesc{
 	ServiceName: "greenfield.storage.Query",
 	HandlerType: (*QueryServer)(nil),
@@ -2515,6 +2675,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{
 			MethodName: "QueryPolicyById",
 			Handler:    _Query_QueryPolicyById_Handler,
 		},
+		{
+			MethodName: "QueryLockFee",
+			Handler:    _Query_QueryLockFee_Handler,
+		},
 	},
 	Streams:  []grpc.StreamDesc{},
 	Metadata: "greenfield/storage/query.proto",
@@ -2819,6 +2983,18 @@ func (m *QueryHeadObjectResponse) MarshalToSizedBuffer(dAtA []byte) (int, error)
 	_ = i
 	var l int
 	_ = l
+	if m.GlobalVirtualGroup != nil {
+		{
+			size, err := m.GlobalVirtualGroup.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintQuery(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0x12
+	}
 	if m.ObjectInfo != nil {
 		{
 			size, err := m.ObjectInfo.MarshalToSizedBuffer(dAtA[:i])
@@ -3714,6 +3890,79 @@ func (m *QueryPolicyByIdResponse) MarshalToSizedBuffer(dAtA []byte) (int, error)
 	return len(dAtA) - i, nil
 }
 
+func (m *QueryLockFeeRequest) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *QueryLockFeeRequest) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryLockFeeRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.PayloadSize != 0 {
+		i = encodeVarintQuery(dAtA, i, uint64(m.PayloadSize))
+		i--
+		dAtA[i] = 0x18
+	}
+	if m.CreateAt != 0 {
+		i = encodeVarintQuery(dAtA, i, uint64(m.CreateAt))
+		i--
+		dAtA[i] = 0x10
+	}
+	if len(m.PrimarySpAddress) > 0 {
+		i -= len(m.PrimarySpAddress)
+		copy(dAtA[i:], m.PrimarySpAddress)
+		i = encodeVarintQuery(dAtA, i, uint64(len(m.PrimarySpAddress)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *QueryLockFeeResponse) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *QueryLockFeeResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryLockFeeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	{
+		size := m.Amount.Size()
+		i -= size
+		if _, err := m.Amount.MarshalTo(dAtA[i:]); err != nil {
+			return 0, err
+		}
+		i = encodeVarintQuery(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0xa
+	return len(dAtA) - i, nil
+}
+
 func encodeVarintQuery(dAtA []byte, offset int, v uint64) int {
 	offset -= sovQuery(v)
 	base := offset
@@ -3847,6 +4096,10 @@ func (m *QueryHeadObjectResponse) Size() (n int) {
 		l = m.ObjectInfo.Size()
 		n += 1 + l + sovQuery(uint64(l))
 	}
+	if m.GlobalVirtualGroup != nil {
+		l = m.GlobalVirtualGroup.Size()
+		n += 1 + l + sovQuery(uint64(l))
+	}
 	return n
 }
 
@@ -4209,6 +4462,36 @@ func (m *QueryPolicyByIdResponse) Size() (n int) {
 	return n
 }
 
+func (m *QueryLockFeeRequest) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.PrimarySpAddress)
+	if l > 0 {
+		n += 1 + l + sovQuery(uint64(l))
+	}
+	if m.CreateAt != 0 {
+		n += 1 + sovQuery(uint64(m.CreateAt))
+	}
+	if m.PayloadSize != 0 {
+		n += 1 + sovQuery(uint64(m.PayloadSize))
+	}
+	return n
+}
+
+func (m *QueryLockFeeResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = m.Amount.Size()
+	n += 1 + l + sovQuery(uint64(l))
+	return n
+}
+
 func sovQuery(x uint64) (n int) {
 	return (math_bits.Len64(x|1) + 6) / 7
 }
@@ -5011,6 +5294,42 @@ func (m *QueryHeadObjectResponse) Unmarshal(dAtA []byte) error {
 				return err
 			}
 			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroup", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthQuery
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.GlobalVirtualGroup == nil {
+				m.GlobalVirtualGroup = &types.GlobalVirtualGroup{}
+			}
+			if err := m.GlobalVirtualGroup.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipQuery(dAtA[iNdEx:])
@@ -6107,7 +6426,7 @@ func (m *QueryPolicyForAccountResponse) Unmarshal(dAtA []byte) error {
 				return io.ErrUnexpectedEOF
 			}
 			if m.Policy == nil {
-				m.Policy = &types.Policy{}
+				m.Policy = &types1.Policy{}
 			}
 			if err := m.Policy.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
 				return err
@@ -6273,7 +6592,7 @@ func (m *QueryVerifyPermissionRequest) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				m.ActionType |= types.ActionType(b&0x7F) << shift
+				m.ActionType |= types1.ActionType(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
@@ -6342,7 +6661,7 @@ func (m *QueryVerifyPermissionResponse) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				m.Effect |= types.Effect(b&0x7F) << shift
+				m.Effect |= types1.Effect(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
@@ -7011,7 +7330,7 @@ func (m *QueryHeadGroupMemberResponse) Unmarshal(dAtA []byte) error {
 				return io.ErrUnexpectedEOF
 			}
 			if m.GroupMember == nil {
-				m.GroupMember = &types.GroupMember{}
+				m.GroupMember = &types1.GroupMember{}
 			}
 			if err := m.GroupMember.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
 				return err
@@ -7211,7 +7530,7 @@ func (m *QueryPolicyForGroupResponse) Unmarshal(dAtA []byte) error {
 				return io.ErrUnexpectedEOF
 			}
 			if m.Policy == nil {
-				m.Policy = &types.Policy{}
+				m.Policy = &types1.Policy{}
 			}
 			if err := m.Policy.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
 				return err
@@ -7379,7 +7698,7 @@ func (m *QueryPolicyByIdResponse) Unmarshal(dAtA []byte) error {
 				return io.ErrUnexpectedEOF
 			}
 			if m.Policy == nil {
-				m.Policy = &types.Policy{}
+				m.Policy = &types1.Policy{}
 			}
 			if err := m.Policy.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
 				return err
@@ -7406,6 +7725,210 @@ func (m *QueryPolicyByIdResponse) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
+func (m *QueryLockFeeRequest) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowQuery
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: QueryLockFeeRequest: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: QueryLockFeeRequest: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field PrimarySpAddress", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthQuery
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.PrimarySpAddress = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field CreateAt", wireType)
+			}
+			m.CreateAt = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.CreateAt |= int64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 3:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field PayloadSize", wireType)
+			}
+			m.PayloadSize = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.PayloadSize |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipQuery(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *QueryLockFeeResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowQuery
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: QueryLockFeeResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: QueryLockFeeResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthQuery
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipQuery(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
 func skipQuery(dAtA []byte) (n int, err error) {
 	l := len(dAtA)
 	iNdEx := 0
diff --git a/x/storage/types/query.pb.gw.go b/x/storage/types/query.pb.gw.go
index 56b37e265..8b05ed2f9 100644
--- a/x/storage/types/query.pb.gw.go
+++ b/x/storage/types/query.pb.gw.go
@@ -13,7 +13,7 @@ import (
 	"io"
 	"net/http"
 
-	types_1 "github.com/bnb-chain/greenfield/x/permission/types"
+	types_2 "github.com/bnb-chain/greenfield/x/permission/types"
 	"github.com/golang/protobuf/descriptor"
 	"github.com/golang/protobuf/proto"
 	"github.com/grpc-ecosystem/grpc-gateway/runtime"
@@ -805,13 +805,13 @@ func request_Query_VerifyPermission_0(ctx context.Context, marshaler runtime.Mar
 		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "action_type")
 	}
 
-	e, err = runtime.Enum(val, types_1.ActionType_value)
+	e, err = runtime.Enum(val, types_2.ActionType_value)
 
 	if err != nil {
 		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "action_type", err)
 	}
 
-	protoReq.ActionType = types_1.ActionType(e)
+	protoReq.ActionType = types_2.ActionType(e)
 
 	if err := req.ParseForm(); err != nil {
 		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
@@ -864,13 +864,13 @@ func local_request_Query_VerifyPermission_0(ctx context.Context, marshaler runti
 		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "action_type")
 	}
 
-	e, err = runtime.Enum(val, types_1.ActionType_value)
+	e, err = runtime.Enum(val, types_2.ActionType_value)
 
 	if err != nil {
 		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "action_type", err)
 	}
 
-	protoReq.ActionType = types_1.ActionType(e)
+	protoReq.ActionType = types_2.ActionType(e)
 
 	if err := req.ParseForm(); err != nil {
 		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
@@ -1198,6 +1198,42 @@ func local_request_Query_QueryPolicyById_0(ctx context.Context, marshaler runtim
 
 }
 
+var (
+	filter_Query_QueryLockFee_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
+)
+
+func request_Query_QueryLockFee_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryLockFeeRequest
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryLockFee_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := client.QueryLockFee(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_Query_QueryLockFee_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryLockFeeRequest
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryLockFee_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := server.QueryLockFee(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
 // RegisterQueryHandlerServer registers the http handlers for service Query to "mux".
 // UnaryRPC     :call QueryServer directly.
 // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
@@ -1641,6 +1677,29 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv
 
 	})
 
+	mux.Handle("GET", pattern_Query_QueryLockFee_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		var stream runtime.ServerTransportStream
+		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_Query_QueryLockFee_0(rctx, inboundMarshaler, server, req, pathParams)
+		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_Query_QueryLockFee_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
 	return nil
 }
 
@@ -2062,6 +2121,26 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie
 
 	})
 
+	mux.Handle("GET", pattern_Query_QueryLockFee_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_Query_QueryLockFee_0(rctx, inboundMarshaler, client, req, pathParams)
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_Query_QueryLockFee_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
 	return nil
 }
 
@@ -2103,6 +2182,8 @@ var (
 	pattern_Query_QueryPolicyForGroup_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 1, 0, 4, 1, 5, 4}, []string{"greenfield", "storage", "policy_for_group", "resource", "principal_group_id"}, "", runtime.AssumeColonVerbOpt(false)))
 
 	pattern_Query_QueryPolicyById_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"greenfield", "storage", "policy_by_id", "policy_id"}, "", runtime.AssumeColonVerbOpt(false)))
+
+	pattern_Query_QueryLockFee_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"greenfield", "storage", "lock_fee"}, "", runtime.AssumeColonVerbOpt(false)))
 )
 
 var (
@@ -2143,4 +2224,6 @@ var (
 	forward_Query_QueryPolicyForGroup_0 = runtime.ForwardResponseMessage
 
 	forward_Query_QueryPolicyById_0 = runtime.ForwardResponseMessage
+
+	forward_Query_QueryLockFee_0 = runtime.ForwardResponseMessage
 )
diff --git a/x/storage/types/tx.pb.go b/x/storage/types/tx.pb.go
index 33ad302be..b5a626cbb 100644
--- a/x/storage/types/tx.pb.go
+++ b/x/storage/types/tx.pb.go
@@ -47,13 +47,13 @@ type MsgCreateBucket struct {
 	// payment_address defines an account address specified by bucket owner to pay the read fee. Default: creator
 	PaymentAddress string `protobuf:"bytes,4,opt,name=payment_address,json=paymentAddress,proto3" json:"payment_address,omitempty"`
 	// primary_sp_address defines the address of primary sp.
-	PrimarySpAddress string `protobuf:"bytes,6,opt,name=primary_sp_address,json=primarySpAddress,proto3" json:"primary_sp_address,omitempty"`
+	PrimarySpAddress string `protobuf:"bytes,5,opt,name=primary_sp_address,json=primarySpAddress,proto3" json:"primary_sp_address,omitempty"`
 	// primary_sp_approval defines the approval info of the primary SP which indicates that primary sp confirm the user's request.
-	PrimarySpApproval *Approval `protobuf:"bytes,7,opt,name=primary_sp_approval,json=primarySpApproval,proto3" json:"primary_sp_approval,omitempty"`
+	PrimarySpApproval *common.Approval `protobuf:"bytes,6,opt,name=primary_sp_approval,json=primarySpApproval,proto3" json:"primary_sp_approval,omitempty"`
 	// charged_read_quota defines the read data that users are charged for, measured in bytes.
 	// The available read data for each user is the sum of the free read data provided by SP and
 	// the ChargeReadQuota specified here.
-	ChargedReadQuota uint64 `protobuf:"varint,8,opt,name=charged_read_quota,json=chargedReadQuota,proto3" json:"charged_read_quota,omitempty"`
+	ChargedReadQuota uint64 `protobuf:"varint,7,opt,name=charged_read_quota,json=chargedReadQuota,proto3" json:"charged_read_quota,omitempty"`
 }
 
 func (m *MsgCreateBucket) Reset()         { *m = MsgCreateBucket{} }
@@ -124,7 +124,7 @@ func (m *MsgCreateBucket) GetPrimarySpAddress() string {
 	return ""
 }
 
-func (m *MsgCreateBucket) GetPrimarySpApproval() *Approval {
+func (m *MsgCreateBucket) GetPrimarySpApproval() *common.Approval {
 	if m != nil {
 		return m.PrimarySpApproval
 	}
@@ -379,13 +379,11 @@ type MsgCreateObject struct {
 	// content_type defines a standard MIME type describing the format of the object.
 	ContentType string `protobuf:"bytes,6,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"`
 	// primary_sp_approval defines the approval info of the primary SP which indicates that primary sp confirm the user's request.
-	PrimarySpApproval *Approval `protobuf:"bytes,7,opt,name=primary_sp_approval,json=primarySpApproval,proto3" json:"primary_sp_approval,omitempty"`
+	PrimarySpApproval *common.Approval `protobuf:"bytes,7,opt,name=primary_sp_approval,json=primarySpApproval,proto3" json:"primary_sp_approval,omitempty"`
 	// expect_checksums defines a list of hashes which was generate by redundancy algorithm.
 	ExpectChecksums [][]byte `protobuf:"bytes,8,rep,name=expect_checksums,json=expectChecksums,proto3" json:"expect_checksums,omitempty"`
 	// redundancy_type can be ec or replica
 	RedundancyType RedundancyType `protobuf:"varint,9,opt,name=redundancy_type,json=redundancyType,proto3,enum=greenfield.storage.RedundancyType" json:"redundancy_type,omitempty"`
-	// expect_secondarySPs defines a list of StorageProvider address, which is optional
-	ExpectSecondarySpAddresses []string `protobuf:"bytes,10,rep,name=expect_secondary_sp_addresses,json=expectSecondarySpAddresses,proto3" json:"expect_secondary_sp_addresses,omitempty"`
 }
 
 func (m *MsgCreateObject) Reset()         { *m = MsgCreateObject{} }
@@ -463,7 +461,7 @@ func (m *MsgCreateObject) GetContentType() string {
 	return ""
 }
 
-func (m *MsgCreateObject) GetPrimarySpApproval() *Approval {
+func (m *MsgCreateObject) GetPrimarySpApproval() *common.Approval {
 	if m != nil {
 		return m.PrimarySpApproval
 	}
@@ -484,13 +482,6 @@ func (m *MsgCreateObject) GetRedundancyType() RedundancyType {
 	return REDUNDANCY_EC_TYPE
 }
 
-func (m *MsgCreateObject) GetExpectSecondarySpAddresses() []string {
-	if m != nil {
-		return m.ExpectSecondarySpAddresses
-	}
-	return nil
-}
-
 type MsgCreateObjectResponse struct {
 	ObjectId Uint `protobuf:"bytes,1,opt,name=object_id,json=objectId,proto3,customtype=Uint" json:"object_id"`
 }
@@ -535,11 +526,11 @@ type MsgSealObject struct {
 	BucketName string `protobuf:"bytes,2,opt,name=bucket_name,json=bucketName,proto3" json:"bucket_name,omitempty"`
 	// object_name defines the name of object to be sealed.
 	ObjectName string `protobuf:"bytes,3,opt,name=object_name,json=objectName,proto3" json:"object_name,omitempty"`
-	// secondary_sp_addresses defines a list of storage provider which store the redundant data.
-	SecondarySpAddresses []string `protobuf:"bytes,4,rep,name=secondary_sp_addresses,json=secondarySpAddresses,proto3" json:"secondary_sp_addresses,omitempty"`
-	// secondary_sp_signatures defines the signature of the secondary sp that can
+	// global_virtual_group_id defines the id of global virtual group
+	GlobalVirtualGroupId uint32 `protobuf:"varint,4,opt,name=global_virtual_group_id,json=globalVirtualGroupId,proto3" json:"global_virtual_group_id,omitempty"`
+	// secondary_sp_bls_agg_signatures defines the aggregate bls signature of the secondary sp that can
 	// acknowledge that the payload data has received and stored.
-	SecondarySpSignatures [][]byte `protobuf:"bytes,5,rep,name=secondary_sp_signatures,json=secondarySpSignatures,proto3" json:"secondary_sp_signatures,omitempty"`
+	SecondarySpBlsAggSignatures []byte `protobuf:"bytes,5,opt,name=secondary_sp_bls_agg_signatures,json=secondarySpBlsAggSignatures,proto3" json:"secondary_sp_bls_agg_signatures,omitempty"`
 }
 
 func (m *MsgSealObject) Reset()         { *m = MsgSealObject{} }
@@ -596,16 +587,16 @@ func (m *MsgSealObject) GetObjectName() string {
 	return ""
 }
 
-func (m *MsgSealObject) GetSecondarySpAddresses() []string {
+func (m *MsgSealObject) GetGlobalVirtualGroupId() uint32 {
 	if m != nil {
-		return m.SecondarySpAddresses
+		return m.GlobalVirtualGroupId
 	}
-	return nil
+	return 0
 }
 
-func (m *MsgSealObject) GetSecondarySpSignatures() [][]byte {
+func (m *MsgSealObject) GetSecondarySpBlsAggSignatures() []byte {
 	if m != nil {
-		return m.SecondarySpSignatures
+		return m.SecondarySpBlsAggSignatures
 	}
 	return nil
 }
@@ -757,7 +748,7 @@ type MsgCopyObject struct {
 	// dst_object_name defines the name of the object which is copied to
 	DstObjectName string `protobuf:"bytes,5,opt,name=dst_object_name,json=dstObjectName,proto3" json:"dst_object_name,omitempty"`
 	// primary_sp_approval defines the approval info of the primary SP which indicates that primary sp confirm the user's request.
-	DstPrimarySpApproval *Approval `protobuf:"bytes,6,opt,name=dst_primary_sp_approval,json=dstPrimarySpApproval,proto3" json:"dst_primary_sp_approval,omitempty"`
+	DstPrimarySpApproval *common.Approval `protobuf:"bytes,6,opt,name=dst_primary_sp_approval,json=dstPrimarySpApproval,proto3" json:"dst_primary_sp_approval,omitempty"`
 }
 
 func (m *MsgCopyObject) Reset()         { *m = MsgCopyObject{} }
@@ -828,7 +819,7 @@ func (m *MsgCopyObject) GetDstObjectName() string {
 	return ""
 }
 
-func (m *MsgCopyObject) GetDstPrimarySpApproval() *Approval {
+func (m *MsgCopyObject) GetDstPrimarySpApproval() *common.Approval {
 	if m != nil {
 		return m.DstPrimarySpApproval
 	}
@@ -2432,7 +2423,6 @@ type MsgUpdateParams struct {
 	// authority is the address that controls the module (defaults to x/gov unless overwritten).
 	Authority string `protobuf:"bytes,1,opt,name=authority,proto3" json:"authority,omitempty"`
 	// params defines the x/storage parameters to update.
-	//
 	// NOTE: All parameters must be supplied.
 	Params Params `protobuf:"bytes,2,opt,name=params,proto3" json:"params"`
 }
@@ -2485,7 +2475,6 @@ func (m *MsgUpdateParams) GetParams() Params {
 }
 
 // MsgUpdateParamsResponse defines the response structure for executing a
-// MsgUpdateParams message.
 type MsgUpdateParamsResponse struct {
 }
 
@@ -2522,6 +2511,315 @@ func (m *MsgUpdateParamsResponse) XXX_DiscardUnknown() {
 
 var xxx_messageInfo_MsgUpdateParamsResponse proto.InternalMessageInfo
 
+// this line is used by starport scaffolding # proto/tx/message
+type MsgMigrateBucket struct {
+	// operator defines the account address of the operator who initial the migrate bucket
+	Operator string `protobuf:"bytes,1,opt,name=operator,proto3" json:"operator,omitempty"`
+	// bucket_name defines the name of the bucket that need to be migrated
+	BucketName string `protobuf:"bytes,2,opt,name=bucket_name,json=bucketName,proto3" json:"bucket_name,omitempty"`
+	// dst_primary_sp_id defines the destination SP for migration
+	DstPrimarySpId uint32 `protobuf:"varint,3,opt,name=dst_primary_sp_id,json=dstPrimarySpId,proto3" json:"dst_primary_sp_id,omitempty"`
+	// dst_primary_sp_approval defines the approval of destination sp
+	DstPrimarySpApproval *common.Approval `protobuf:"bytes,4,opt,name=dst_primary_sp_approval,json=dstPrimarySpApproval,proto3" json:"dst_primary_sp_approval,omitempty"`
+}
+
+func (m *MsgMigrateBucket) Reset()         { *m = MsgMigrateBucket{} }
+func (m *MsgMigrateBucket) String() string { return proto.CompactTextString(m) }
+func (*MsgMigrateBucket) ProtoMessage()    {}
+func (*MsgMigrateBucket) Descriptor() ([]byte, []int) {
+	return fileDescriptor_ddb71b028305a3cc, []int{46}
+}
+func (m *MsgMigrateBucket) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgMigrateBucket) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgMigrateBucket.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MsgMigrateBucket) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgMigrateBucket.Merge(m, src)
+}
+func (m *MsgMigrateBucket) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgMigrateBucket) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgMigrateBucket.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgMigrateBucket proto.InternalMessageInfo
+
+func (m *MsgMigrateBucket) GetOperator() string {
+	if m != nil {
+		return m.Operator
+	}
+	return ""
+}
+
+func (m *MsgMigrateBucket) GetBucketName() string {
+	if m != nil {
+		return m.BucketName
+	}
+	return ""
+}
+
+func (m *MsgMigrateBucket) GetDstPrimarySpId() uint32 {
+	if m != nil {
+		return m.DstPrimarySpId
+	}
+	return 0
+}
+
+func (m *MsgMigrateBucket) GetDstPrimarySpApproval() *common.Approval {
+	if m != nil {
+		return m.DstPrimarySpApproval
+	}
+	return nil
+}
+
+type MsgMigrateBucketResponse struct {
+}
+
+func (m *MsgMigrateBucketResponse) Reset()         { *m = MsgMigrateBucketResponse{} }
+func (m *MsgMigrateBucketResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgMigrateBucketResponse) ProtoMessage()    {}
+func (*MsgMigrateBucketResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_ddb71b028305a3cc, []int{47}
+}
+func (m *MsgMigrateBucketResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgMigrateBucketResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgMigrateBucketResponse.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MsgMigrateBucketResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgMigrateBucketResponse.Merge(m, src)
+}
+func (m *MsgMigrateBucketResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgMigrateBucketResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgMigrateBucketResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgMigrateBucketResponse proto.InternalMessageInfo
+
+type MsgCompleteMigrateBucket struct {
+	// operator defines the account address of the msg operator.
+	// The CompleteMigrateBucket transaction must be initiated by the destination SP of the migration
+	Operator string `protobuf:"bytes,1,opt,name=operator,proto3" json:"operator,omitempty"`
+	// bucket_name defines the name of the bucket that need to be migrated
+	BucketName string `protobuf:"bytes,2,opt,name=bucket_name,json=bucketName,proto3" json:"bucket_name,omitempty"`
+	// global_virtual_group_family_id defines the family id which the bucket migrate to
+	GlobalVirtualGroupFamilyId uint32 `protobuf:"varint,3,opt,name=global_virtual_group_family_id,json=globalVirtualGroupFamilyId,proto3" json:"global_virtual_group_family_id,omitempty"`
+	// gvg_mappings defines the src and dst gvg mapping relationships which the bucket migrate to
+	GvgMappings []*GVGMapping `protobuf:"bytes,4,rep,name=gvg_mappings,json=gvgMappings,proto3" json:"gvg_mappings,omitempty"`
+}
+
+func (m *MsgCompleteMigrateBucket) Reset()         { *m = MsgCompleteMigrateBucket{} }
+func (m *MsgCompleteMigrateBucket) String() string { return proto.CompactTextString(m) }
+func (*MsgCompleteMigrateBucket) ProtoMessage()    {}
+func (*MsgCompleteMigrateBucket) Descriptor() ([]byte, []int) {
+	return fileDescriptor_ddb71b028305a3cc, []int{48}
+}
+func (m *MsgCompleteMigrateBucket) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgCompleteMigrateBucket) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgCompleteMigrateBucket.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MsgCompleteMigrateBucket) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgCompleteMigrateBucket.Merge(m, src)
+}
+func (m *MsgCompleteMigrateBucket) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgCompleteMigrateBucket) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgCompleteMigrateBucket.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgCompleteMigrateBucket proto.InternalMessageInfo
+
+func (m *MsgCompleteMigrateBucket) GetOperator() string {
+	if m != nil {
+		return m.Operator
+	}
+	return ""
+}
+
+func (m *MsgCompleteMigrateBucket) GetBucketName() string {
+	if m != nil {
+		return m.BucketName
+	}
+	return ""
+}
+
+func (m *MsgCompleteMigrateBucket) GetGlobalVirtualGroupFamilyId() uint32 {
+	if m != nil {
+		return m.GlobalVirtualGroupFamilyId
+	}
+	return 0
+}
+
+func (m *MsgCompleteMigrateBucket) GetGvgMappings() []*GVGMapping {
+	if m != nil {
+		return m.GvgMappings
+	}
+	return nil
+}
+
+type MsgCompleteMigrateBucketResponse struct {
+}
+
+func (m *MsgCompleteMigrateBucketResponse) Reset()         { *m = MsgCompleteMigrateBucketResponse{} }
+func (m *MsgCompleteMigrateBucketResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgCompleteMigrateBucketResponse) ProtoMessage()    {}
+func (*MsgCompleteMigrateBucketResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_ddb71b028305a3cc, []int{49}
+}
+func (m *MsgCompleteMigrateBucketResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgCompleteMigrateBucketResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgCompleteMigrateBucketResponse.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MsgCompleteMigrateBucketResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgCompleteMigrateBucketResponse.Merge(m, src)
+}
+func (m *MsgCompleteMigrateBucketResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgCompleteMigrateBucketResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgCompleteMigrateBucketResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgCompleteMigrateBucketResponse proto.InternalMessageInfo
+
+type MsgCancelMigrateBucket struct {
+	// operator defines the account address of the msg operator.
+	// Only the user can send this transaction to cancel the migrate bucket
+	Operator string `protobuf:"bytes,1,opt,name=operator,proto3" json:"operator,omitempty"`
+	// bucket_name defines the name of the bucket that need to be migrated
+	BucketName string `protobuf:"bytes,2,opt,name=bucket_name,json=bucketName,proto3" json:"bucket_name,omitempty"`
+}
+
+func (m *MsgCancelMigrateBucket) Reset()         { *m = MsgCancelMigrateBucket{} }
+func (m *MsgCancelMigrateBucket) String() string { return proto.CompactTextString(m) }
+func (*MsgCancelMigrateBucket) ProtoMessage()    {}
+func (*MsgCancelMigrateBucket) Descriptor() ([]byte, []int) {
+	return fileDescriptor_ddb71b028305a3cc, []int{50}
+}
+func (m *MsgCancelMigrateBucket) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgCancelMigrateBucket) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgCancelMigrateBucket.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MsgCancelMigrateBucket) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgCancelMigrateBucket.Merge(m, src)
+}
+func (m *MsgCancelMigrateBucket) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgCancelMigrateBucket) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgCancelMigrateBucket.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgCancelMigrateBucket proto.InternalMessageInfo
+
+func (m *MsgCancelMigrateBucket) GetOperator() string {
+	if m != nil {
+		return m.Operator
+	}
+	return ""
+}
+
+func (m *MsgCancelMigrateBucket) GetBucketName() string {
+	if m != nil {
+		return m.BucketName
+	}
+	return ""
+}
+
+type MsgCancelMigrateBucketResponse struct {
+}
+
+func (m *MsgCancelMigrateBucketResponse) Reset()         { *m = MsgCancelMigrateBucketResponse{} }
+func (m *MsgCancelMigrateBucketResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgCancelMigrateBucketResponse) ProtoMessage()    {}
+func (*MsgCancelMigrateBucketResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_ddb71b028305a3cc, []int{51}
+}
+func (m *MsgCancelMigrateBucketResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgCancelMigrateBucketResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgCancelMigrateBucketResponse.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MsgCancelMigrateBucketResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgCancelMigrateBucketResponse.Merge(m, src)
+}
+func (m *MsgCancelMigrateBucketResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgCancelMigrateBucketResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgCancelMigrateBucketResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgCancelMigrateBucketResponse proto.InternalMessageInfo
+
 func init() {
 	proto.RegisterType((*MsgCreateBucket)(nil), "greenfield.storage.MsgCreateBucket")
 	proto.RegisterType((*MsgCreateBucketResponse)(nil), "greenfield.storage.MsgCreateBucketResponse")
@@ -2569,134 +2867,153 @@ func init() {
 	proto.RegisterType((*MsgMirrorGroupResponse)(nil), "greenfield.storage.MsgMirrorGroupResponse")
 	proto.RegisterType((*MsgUpdateParams)(nil), "greenfield.storage.MsgUpdateParams")
 	proto.RegisterType((*MsgUpdateParamsResponse)(nil), "greenfield.storage.MsgUpdateParamsResponse")
+	proto.RegisterType((*MsgMigrateBucket)(nil), "greenfield.storage.MsgMigrateBucket")
+	proto.RegisterType((*MsgMigrateBucketResponse)(nil), "greenfield.storage.MsgMigrateBucketResponse")
+	proto.RegisterType((*MsgCompleteMigrateBucket)(nil), "greenfield.storage.MsgCompleteMigrateBucket")
+	proto.RegisterType((*MsgCompleteMigrateBucketResponse)(nil), "greenfield.storage.MsgCompleteMigrateBucketResponse")
+	proto.RegisterType((*MsgCancelMigrateBucket)(nil), "greenfield.storage.MsgCancelMigrateBucket")
+	proto.RegisterType((*MsgCancelMigrateBucketResponse)(nil), "greenfield.storage.MsgCancelMigrateBucketResponse")
 }
 
 func init() { proto.RegisterFile("greenfield/storage/tx.proto", fileDescriptor_ddb71b028305a3cc) }
 
 var fileDescriptor_ddb71b028305a3cc = []byte{
-	// 1949 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x5a, 0x4f, 0x6f, 0x1b, 0xc7,
-	0x15, 0xf7, 0x8a, 0xb4, 0x2c, 0x3e, 0xca, 0x92, 0xbd, 0x51, 0x2c, 0x86, 0x8e, 0x25, 0x99, 0x01,
-	0x12, 0x26, 0xae, 0x49, 0x47, 0x0d, 0x8c, 0x54, 0x87, 0xa0, 0x96, 0xd3, 0x16, 0x42, 0xcd, 0x58,
-	0x59, 0xda, 0x29, 0x90, 0xa2, 0x60, 0x96, 0xbb, 0xe3, 0xd5, 0x26, 0xdc, 0x9d, 0xed, 0xcc, 0xd2,
-	0x36, 0x73, 0xec, 0x27, 0x08, 0xd0, 0x1e, 0x7a, 0x28, 0x0c, 0x14, 0xe8, 0xa1, 0xb7, 0x16, 0x45,
-	0x6e, 0x3d, 0xb4, 0x28, 0x50, 0xc0, 0xe8, 0xc9, 0xc8, 0xa9, 0xe8, 0xc1, 0x0d, 0xec, 0x43, 0xbf,
-	0x46, 0xb1, 0x33, 0xb3, 0xbb, 0xb3, 0xff, 0x19, 0x59, 0x82, 0x75, 0xb2, 0x77, 0xe6, 0x37, 0x6f,
-	0xde, 0xff, 0xf7, 0xe6, 0x51, 0x70, 0xd1, 0x22, 0x08, 0xb9, 0xf7, 0x6c, 0x34, 0x31, 0xfb, 0xd4,
-	0xc7, 0x44, 0xb7, 0x50, 0xdf, 0x7f, 0xd8, 0xf3, 0x08, 0xf6, 0xb1, 0xaa, 0xc6, 0x9b, 0x3d, 0xb1,
-	0xd9, 0x5e, 0x37, 0x30, 0x75, 0x30, 0xed, 0x3b, 0xd4, 0xea, 0xdf, 0x7f, 0x37, 0xf8, 0x87, 0x83,
-	0xdb, 0xaf, 0xf1, 0x8d, 0x11, 0xfb, 0xea, 0xf3, 0x0f, 0xb1, 0xb5, 0x66, 0x61, 0x0b, 0xf3, 0xf5,
-	0xe0, 0x7f, 0x62, 0x75, 0xd3, 0xc2, 0xd8, 0x9a, 0xa0, 0x3e, 0xfb, 0x1a, 0x4f, 0xef, 0xf5, 0x7d,
-	0xdb, 0x41, 0xd4, 0xd7, 0x1d, 0x2f, 0x02, 0xc4, 0xbc, 0x19, 0xd8, 0x71, 0xb0, 0xdb, 0x7f, 0x40,
-	0x74, 0xcf, 0x43, 0x44, 0x00, 0x3a, 0x12, 0xc0, 0x43, 0xc4, 0xb1, 0x29, 0xb5, 0xb1, 0x2b, 0xb0,
-	0x39, 0x44, 0x42, 0x01, 0x2b, 0x01, 0x9e, 0x4e, 0x74, 0x47, 0x70, 0xdf, 0xf9, 0x5b, 0x0d, 0x56,
-	0x07, 0xd4, 0xba, 0x49, 0x90, 0xee, 0xa3, 0xdd, 0xa9, 0xf1, 0x05, 0xf2, 0xd5, 0x6d, 0x38, 0x63,
-	0x04, 0xdf, 0x98, 0xb4, 0x94, 0x2d, 0xa5, 0xdb, 0xd8, 0x6d, 0x7d, 0xf3, 0xf5, 0xd5, 0x35, 0x21,
-	0xf4, 0x0d, 0xd3, 0x24, 0x88, 0xd2, 0xa1, 0x4f, 0x6c, 0xd7, 0xd2, 0x42, 0xa0, 0xba, 0x09, 0xcd,
-	0x31, 0x3b, 0x3d, 0x72, 0x75, 0x07, 0xb5, 0x16, 0x82, 0x73, 0x1a, 0xf0, 0xa5, 0x8f, 0x74, 0x07,
-	0xa9, 0xbb, 0x00, 0xf7, 0x6d, 0x6a, 0x8f, 0xed, 0x89, 0xed, 0xcf, 0x5a, 0xb5, 0x2d, 0xa5, 0xbb,
-	0xb2, 0xdd, 0xe9, 0x65, 0x6d, 0xd0, 0xfb, 0x24, 0x42, 0xdd, 0x99, 0x79, 0x48, 0x93, 0x4e, 0xa9,
-	0x37, 0x60, 0xd5, 0xd3, 0x67, 0x0e, 0x72, 0xfd, 0x91, 0xce, 0xd9, 0x68, 0xd5, 0x2b, 0x18, 0x5c,
-	0x11, 0x07, 0xc4, 0xaa, 0xfa, 0x63, 0x50, 0x3d, 0x62, 0x3b, 0x3a, 0x99, 0x8d, 0xa8, 0x17, 0x51,
-	0x59, 0xac, 0xa0, 0x72, 0x4e, 0x9c, 0x19, 0x7a, 0x21, 0x9d, 0x5b, 0xf0, 0x8a, 0x4c, 0xc7, 0xf3,
-	0x08, 0xbe, 0xaf, 0x4f, 0x5a, 0x67, 0xb6, 0x94, 0x6e, 0x73, 0xfb, 0xf5, 0x3c, 0xb9, 0x6e, 0x08,
-	0x8c, 0x76, 0x3e, 0x26, 0x26, 0x96, 0xd4, 0xef, 0x81, 0x6a, 0x1c, 0xe8, 0xc4, 0x42, 0xe6, 0x88,
-	0x20, 0xdd, 0x1c, 0xfd, 0x72, 0x8a, 0x7d, 0xbd, 0xb5, 0xb4, 0xa5, 0x74, 0xeb, 0xda, 0x39, 0xb1,
-	0xa3, 0x21, 0xdd, 0xfc, 0x38, 0x58, 0xdf, 0x59, 0xfe, 0xd5, 0xff, 0xfe, 0xfc, 0x4e, 0xa8, 0xf9,
-	0xce, 0x10, 0xd6, 0x53, 0x06, 0xd4, 0x10, 0xf5, 0xb0, 0x4b, 0x91, 0xfa, 0x3e, 0x34, 0x84, 0x51,
-	0x6c, 0x53, 0x98, 0xf2, 0xe2, 0xe3, 0xa7, 0x9b, 0xa7, 0xfe, 0xf3, 0x74, 0xb3, 0x7e, 0xd7, 0x76,
-	0xfd, 0x6f, 0xbe, 0xbe, 0xda, 0x14, 0xf2, 0x06, 0x9f, 0xda, 0x12, 0x47, 0xef, 0x99, 0x9d, 0x07,
-	0xcc, 0x2b, 0x3e, 0x44, 0x13, 0x14, 0x79, 0xc5, 0x7b, 0xb0, 0x84, 0x3d, 0x44, 0xe6, 0x72, 0x8b,
-	0x08, 0x59, 0xe9, 0x17, 0x3b, 0x67, 0x03, 0x61, 0x22, 0x7c, 0xe7, 0x35, 0x26, 0x8d, 0x7c, 0x71,
-	0x28, 0x4d, 0xe7, 0x37, 0x0a, 0xac, 0x05, 0x7b, 0x36, 0x35, 0xb0, 0xeb, 0xdb, 0xee, 0xf4, 0x78,
-	0x39, 0x53, 0x2f, 0xc0, 0x22, 0x41, 0x3a, 0xc5, 0x2e, 0xf3, 0xd6, 0x86, 0x26, 0xbe, 0xd2, 0x1c,
-	0x6f, 0xc0, 0xeb, 0x79, 0x5c, 0x45, 0x6c, 0xff, 0xa3, 0x2e, 0x45, 0xd8, 0xed, 0xf1, 0xe7, 0xc8,
-	0x38, 0xa6, 0x08, 0xdb, 0x84, 0x26, 0x66, 0xe4, 0x39, 0x80, 0x33, 0x0d, 0x7c, 0x89, 0x01, 0x2e,
-	0xc3, 0xb2, 0xa7, 0xcf, 0x26, 0x58, 0x37, 0x47, 0xd4, 0xfe, 0x12, 0xb1, 0xd8, 0xa9, 0x6b, 0x4d,
-	0xb1, 0x36, 0xb4, 0xbf, 0x4c, 0x47, 0xe9, 0xe9, 0x43, 0x45, 0xe9, 0x65, 0x58, 0x0e, 0x54, 0x11,
-	0x44, 0xa9, 0x3f, 0xf3, 0x10, 0x0f, 0x2e, 0xad, 0x29, 0xd6, 0x02, 0xf8, 0x11, 0x47, 0xcf, 0xdb,
-	0x70, 0x0e, 0x3d, 0xf4, 0x02, 0xc1, 0x8d, 0x03, 0x64, 0x7c, 0x41, 0xa7, 0x0e, 0x6d, 0x2d, 0x6d,
-	0xd5, 0xba, 0xcb, 0xda, 0x2a, 0x5f, 0xbf, 0x19, 0x2e, 0xab, 0x3f, 0x85, 0x55, 0x82, 0xcc, 0xa9,
-	0x6b, 0xea, 0xae, 0x31, 0xe3, 0xec, 0x35, 0x8a, 0x85, 0xd4, 0x22, 0x28, 0x13, 0x72, 0x85, 0x24,
-	0xbe, 0xd5, 0x9f, 0xc3, 0x25, 0x71, 0x2f, 0x45, 0x06, 0x76, 0xcd, 0x64, 0x52, 0x41, 0xb4, 0x05,
-	0x5b, 0xb5, 0x52, 0xdb, 0xb6, 0xf9, 0xf1, 0x61, 0x78, 0x3a, 0x4a, 0x2f, 0x88, 0x96, 0x04, 0x39,
-	0xf7, 0x21, 0x39, 0xc8, 0x85, 0xd9, 0xe7, 0x0c, 0x72, 0x8e, 0xde, 0x33, 0x3b, 0x8f, 0x16, 0xe0,
-	0xec, 0x80, 0x5a, 0x43, 0xa4, 0x4f, 0x84, 0x5f, 0x1e, 0x53, 0x24, 0x55, 0x7a, 0xe6, 0x47, 0x70,
-	0xa1, 0x40, 0x85, 0xf5, 0x0a, 0x15, 0xae, 0xd1, 0x1c, 0xe5, 0xa9, 0xd7, 0x61, 0x3d, 0x41, 0x8f,
-	0xda, 0x96, 0xab, 0xfb, 0x53, 0x82, 0x68, 0xeb, 0x34, 0x73, 0x8c, 0x57, 0xa5, 0x63, 0xc3, 0x68,
-	0x33, 0x1d, 0xda, 0xeb, 0xf0, 0x6a, 0x42, 0x3f, 0x51, 0x4c, 0xff, 0x4e, 0x81, 0x57, 0x06, 0xd4,
-	0xd2, 0xd0, 0xe7, 0xcc, 0x7c, 0x2f, 0x5b, 0x7f, 0x69, 0xbe, 0x2f, 0xc1, 0xc5, 0x1c, 0xee, 0x22,
-	0xee, 0xff, 0xc4, 0xed, 0x7e, 0x13, 0x7b, 0x33, 0xc1, 0x77, 0x3b, 0xcd, 0xb7, 0xc4, 0xdd, 0x9b,
-	0xb0, 0x4a, 0x89, 0x31, 0xca, 0x72, 0x78, 0x96, 0x12, 0x63, 0x37, 0x66, 0xf2, 0x4d, 0x58, 0x35,
-	0xa9, 0x9f, 0xc0, 0x71, 0x46, 0xcf, 0x9a, 0xd4, 0x4f, 0xe2, 0x02, 0x7a, 0xb2, 0x40, 0xf5, 0x88,
-	0xde, 0xed, 0xd8, 0x27, 0x04, 0x3d, 0x19, 0x77, 0x3a, 0xa2, 0x27, 0xe1, 0x86, 0xb0, 0x1e, 0xe0,
-	0xf2, 0xf2, 0xc9, 0xe2, 0x1c, 0xf9, 0x64, 0xcd, 0xa4, 0xfe, 0x7e, 0x3a, 0xa5, 0xa4, 0x15, 0xfa,
-	0x31, 0x73, 0x84, 0x58, 0x61, 0x47, 0x10, 0x7c, 0xbf, 0x55, 0xa4, 0x12, 0x7b, 0xb2, 0xdc, 0x47,
-	0xae, 0xc1, 0x29, 0xd7, 0x79, 0x92, 0xa9, 0xc1, 0xc7, 0xcb, 0xfa, 0x0e, 0x40, 0xa4, 0x5f, 0xda,
-	0xaa, 0xb1, 0x64, 0x50, 0xaa, 0xe0, 0x46, 0xa8, 0x60, 0x2a, 0xd5, 0xef, 0xfa, 0x77, 0xaa, 0xdf,
-	0x29, 0x91, 0xff, 0xaa, 0xc0, 0x4a, 0x94, 0x7b, 0x7f, 0x42, 0xf0, 0xd4, 0x3b, 0x54, 0xf9, 0xbe,
-	0x04, 0x60, 0x05, 0x87, 0x65, 0x49, 0x1b, 0x6c, 0x85, 0x09, 0xba, 0x0d, 0x67, 0x1c, 0xe4, 0x8c,
-	0x11, 0x09, 0xa5, 0x2c, 0x21, 0x29, 0x80, 0xea, 0x1a, 0x9c, 0x46, 0x0f, 0x7d, 0xa2, 0x0b, 0xf9,
-	0xf8, 0x47, 0xaa, 0x70, 0xec, 0xc3, 0x85, 0x24, 0xf3, 0x91, 0xeb, 0x5e, 0x87, 0x25, 0xce, 0xd0,
-	0x7c, 0x9e, 0x7b, 0x86, 0x81, 0xf7, 0xcc, 0x8e, 0xcf, 0xd4, 0xc1, 0xbd, 0x83, 0xab, 0xe3, 0x70,
-	0xb6, 0x2f, 0x57, 0x48, 0xda, 0x4a, 0x2d, 0x26, 0x87, 0x74, 0x6b, 0x6c, 0x9f, 0x05, 0xe6, 0x92,
-	0x77, 0x3d, 0x33, 0x14, 0x71, 0xc0, 0xf4, 0x73, 0x48, 0xb6, 0x7e, 0x00, 0x4d, 0xce, 0x16, 0x7e,
-	0xe0, 0x22, 0xc2, 0xf9, 0x2a, 0x39, 0xc8, 0x65, 0xb8, 0x1d, 0x60, 0x53, 0x12, 0xd5, 0xd2, 0x26,
-	0xfe, 0x00, 0x56, 0x84, 0xe5, 0x46, 0x3e, 0x0e, 0x4a, 0x5c, 0x65, 0x71, 0x5b, 0x16, 0xf8, 0x3b,
-	0xf8, 0x86, 0x69, 0xaa, 0x1f, 0xc2, 0x79, 0xe9, 0xbc, 0xc9, 0x54, 0xc1, 0xca, 0x59, 0x19, 0x89,
-	0xd5, 0x88, 0x04, 0xd7, 0x5d, 0xbe, 0xf7, 0x67, 0x94, 0x17, 0x69, 0xf7, 0x5f, 0xbc, 0xd2, 0x49,
-	0x80, 0x1f, 0x05, 0x5e, 0x76, 0xe2, 0x94, 0x9b, 0x1f, 0x0b, 0xb9, 0x75, 0x31, 0x2d, 0x4b, 0x24,
-	0xeb, 0x1f, 0x15, 0x56, 0x17, 0x6f, 0x21, 0xfd, 0xbe, 0xf0, 0xec, 0x6b, 0xb0, 0xc8, 0xf5, 0x57,
-	0x29, 0xa3, 0xc0, 0x1d, 0x9f, 0x84, 0x3b, 0xcd, 0x40, 0x16, 0x71, 0x8d, 0xe8, 0x4c, 0x62, 0x4e,
-	0xe3, 0x04, 0xbd, 0x20, 0xd9, 0x8b, 0x57, 0xdd, 0x3d, 0xf7, 0x1e, 0x3e, 0xae, 0xfc, 0x7c, 0x2b,
-	0xf7, 0xe1, 0x5a, 0x63, 0x75, 0x77, 0x43, 0xae, 0xbb, 0x62, 0x2a, 0x71, 0x77, 0xcf, 0xf5, 0xaf,
-	0xbf, 0xf7, 0x89, 0x3e, 0x99, 0xa2, 0xec, 0xc3, 0xf6, 0x28, 0xde, 0xf7, 0x47, 0xf0, 0x80, 0x29,
-	0xf3, 0x9a, 0x58, 0xa3, 0x91, 0xc6, 0x1f, 0x29, 0xbc, 0x39, 0xd0, 0x5d, 0x03, 0x4d, 0x12, 0xaf,
-	0xbc, 0x13, 0x52, 0xce, 0x37, 0xe1, 0x52, 0x2e, 0x7f, 0x91, 0x04, 0x7f, 0x5f, 0x80, 0xe5, 0x01,
-	0xb5, 0xf6, 0xa7, 0xfe, 0x3e, 0x9e, 0xd8, 0xc6, 0xec, 0x90, 0x8c, 0x7f, 0x00, 0x0d, 0x8f, 0xd8,
-	0xae, 0x61, 0x7b, 0xfa, 0x84, 0xb1, 0xdd, 0xdc, 0xde, 0x92, 0x35, 0x1f, 0x0f, 0xb1, 0x7a, 0xfb,
-	0x21, 0x4e, 0x8b, 0x8f, 0x04, 0x4d, 0x28, 0x41, 0x14, 0x4f, 0x89, 0x11, 0x0a, 0x15, 0x7d, 0xab,
-	0x3f, 0x04, 0xa0, 0xbe, 0xee, 0xa3, 0xc0, 0xd4, 0xfc, 0x51, 0x50, 0x4c, 0x7c, 0x18, 0x02, 0x35,
-	0xe9, 0x8c, 0x3a, 0x80, 0xe0, 0x31, 0x68, 0x13, 0xdd, 0xb7, 0xb1, 0x3b, 0xf2, 0x6d, 0x07, 0x89,
-	0xe7, 0x66, 0xbb, 0xc7, 0x47, 0x75, 0xbd, 0x70, 0x54, 0xd7, 0xbb, 0x13, 0x8e, 0xea, 0x76, 0x97,
-	0x1e, 0x3f, 0xdd, 0x54, 0xbe, 0xfa, 0xef, 0xa6, 0xa2, 0xad, 0xc4, 0x87, 0x83, 0xed, 0xb4, 0x8e,
-	0xf7, 0x59, 0x0d, 0x8a, 0x34, 0x28, 0xf7, 0x87, 0x1e, 0x5b, 0x09, 0xaa, 0x6c, 0x7d, 0x8e, 0xfe,
-	0x90, 0xa3, 0xf7, 0xcc, 0xce, 0x5f, 0xe4, 0xfe, 0xf0, 0xa4, 0xda, 0x25, 0xad, 0x86, 0xa1, 0xd4,
-	0x39, 0x1e, 0x99, 0x26, 0xfe, 0xc9, 0x35, 0x31, 0xb0, 0x09, 0xc1, 0xe4, 0x85, 0x42, 0xeb, 0x0a,
-	0x2c, 0xd8, 0xa6, 0xc8, 0xc9, 0xa5, 0x97, 0x2f, 0xd8, 0x66, 0x3a, 0x0e, 0x6b, 0x55, 0x71, 0x58,
-	0x9f, 0xaf, 0xad, 0x96, 0xc5, 0x88, 0x22, 0xf0, 0x0f, 0xb2, 0x88, 0x2f, 0x34, 0xd5, 0x3a, 0x52,
-	0x11, 0xcb, 0x32, 0x21, 0x97, 0x20, 0x91, 0x09, 0xbf, 0x95, 0x7b, 0x85, 0x78, 0xff, 0xa5, 0x4d,
-	0x15, 0x92, 0xb5, 0xa0, 0x7e, 0x14, 0xb5, 0x40, 0xb6, 0x61, 0x6a, 0xce, 0xf7, 0x7b, 0xfe, 0x4e,
-	0xe0, 0x7b, 0x2f, 0xd2, 0x18, 0x7f, 0x27, 0x13, 0x56, 0x34, 0x0d, 0xb9, 0x5d, 0xb4, 0xc4, 0x62,
-	0xc4, 0xfd, 0xaf, 0xb9, 0x07, 0x72, 0xdb, 0xed, 0xb3, 0x5f, 0x08, 0xd4, 0xeb, 0xd0, 0xd0, 0xa7,
-	0xfe, 0x01, 0x26, 0x81, 0xfa, 0xaa, 0xf8, 0x8f, 0xa1, 0xea, 0xfb, 0xb0, 0xc8, 0x7f, 0x63, 0x10,
-	0xd9, 0xa6, 0x9d, 0xa7, 0x73, 0x7e, 0xc7, 0x6e, 0x3d, 0x10, 0x50, 0x13, 0xf8, 0x9d, 0x95, 0x80,
-	0xdd, 0x98, 0x92, 0x50, 0xb7, 0xcc, 0x54, 0xc8, 0xf0, 0xf6, 0x23, 0x15, 0x6a, 0x03, 0x6a, 0xa9,
-	0x9f, 0xc1, 0x72, 0xe2, 0xc7, 0x8b, 0x37, 0xf2, 0x2e, 0x4b, 0x0d, 0xc8, 0xdb, 0x57, 0xe6, 0x00,
-	0x45, 0x99, 0xeb, 0x33, 0x58, 0x4e, 0x0c, 0xc2, 0x8b, 0x6e, 0x90, 0x41, 0x85, 0x37, 0xe4, 0x4d,
-	0xb6, 0xd5, 0x09, 0x9c, 0xcb, 0x34, 0x6c, 0x6f, 0x15, 0x10, 0x48, 0x03, 0xdb, 0xfd, 0x39, 0x81,
-	0xb2, 0x3c, 0x89, 0x44, 0x53, 0x24, 0x8f, 0x0c, 0x2a, 0x94, 0x27, 0x2f, 0x14, 0x54, 0x0c, 0xe7,
-	0xb3, 0x53, 0xfa, 0x6e, 0x91, 0x46, 0xd2, 0xc8, 0xf6, 0xb5, 0x79, 0x91, 0xb2, 0x48, 0x89, 0xce,
-	0xab, 0xdc, 0x09, 0x38, 0xa8, 0xc2, 0x09, 0x52, 0x83, 0x9e, 0x4f, 0x01, 0xa4, 0x39, 0xdf, 0xe5,
-	0x82, 0xa3, 0x31, 0xa4, 0xfd, 0x76, 0x25, 0x44, 0x36, 0x7f, 0x66, 0x92, 0x58, 0x64, 0xfe, 0x34,
-	0xb0, 0xd0, 0xfc, 0x45, 0xd3, 0xbf, 0x40, 0x12, 0x69, 0xf2, 0x57, 0x24, 0x49, 0x0c, 0x29, 0x94,
-	0x24, 0x67, 0x1c, 0x16, 0x85, 0x4a, 0x85, 0x1d, 0x64, 0x50, 0x45, 0xa8, 0xa4, 0x6e, 0x20, 0xa0,
-	0xe6, 0x74, 0xda, 0x85, 0x2c, 0x66, 0xa0, 0xed, 0x77, 0xe7, 0x86, 0x66, 0x03, 0xa6, 0x42, 0x2a,
-	0x19, 0x54, 0x11, 0x30, 0xa9, 0x1b, 0x92, 0x01, 0x23, 0xae, 0x99, 0x23, 0x60, 0xc4, 0x5d, 0xd7,
-	0xe6, 0x45, 0x66, 0x33, 0x8e, 0x54, 0xa6, 0xcb, 0x33, 0x4e, 0x0c, 0xac, 0xc8, 0x38, 0xd9, 0xc6,
-	0x40, 0xfd, 0x05, 0x34, 0xe5, 0xf1, 0x59, 0xa7, 0x34, 0xf0, 0x18, 0xa6, 0xfd, 0x4e, 0x35, 0x46,
-	0x26, 0x2f, 0x8f, 0xa3, 0x3a, 0xa5, 0xfe, 0x54, 0x4e, 0x3e, 0x67, 0xc0, 0x14, 0x18, 0x27, 0x3b,
-	0x5c, 0xea, 0x96, 0xea, 0x40, 0x42, 0x16, 0x1a, 0xa7, 0x70, 0xe6, 0x12, 0x1b, 0x47, 0x9a, 0xb7,
-	0xbc, 0x55, 0x4d, 0x85, 0x01, 0x2b, 0x8c, 0x93, 0x9d, 0x7a, 0x04, 0xf9, 0x40, 0x9a, 0x78, 0x14,
-	0xe5, 0x83, 0x18, 0x52, 0x98, 0x0f, 0xb2, 0xd3, 0x88, 0xc0, 0x32, 0x72, 0x3f, 0xd4, 0x29, 0x8d,
-	0x89, 0x72, 0xcb, 0xe4, 0x34, 0x2d, 0xea, 0xcf, 0xa0, 0x11, 0x3f, 0x5a, 0xb7, 0x0a, 0x0e, 0x46,
-	0x88, 0x76, 0xb7, 0x0a, 0x91, 0xcd, 0x63, 0x82, 0x76, 0x79, 0x1e, 0x13, 0xe4, 0xaf, 0xcc, 0x01,
-	0x92, 0x6f, 0x48, 0xf4, 0x5a, 0x6f, 0x94, 0x9a, 0x8d, 0x83, 0x0a, 0x6f, 0xc8, 0x6b, 0x90, 0x76,
-	0xf7, 0x1e, 0x3f, 0xdb, 0x50, 0x9e, 0x3c, 0xdb, 0x50, 0xbe, 0x7d, 0xb6, 0xa1, 0x7c, 0xf5, 0x7c,
-	0xe3, 0xd4, 0x93, 0xe7, 0x1b, 0xa7, 0xfe, 0xfd, 0x7c, 0xe3, 0xd4, 0xa7, 0x7d, 0xcb, 0xf6, 0x0f,
-	0xa6, 0xe3, 0x9e, 0x81, 0x9d, 0xfe, 0xd8, 0x1d, 0x5f, 0x35, 0x0e, 0x74, 0xdb, 0xed, 0x4b, 0x7f,
-	0x29, 0xf2, 0x30, 0xfe, 0x6b, 0x99, 0x99, 0x87, 0xe8, 0x78, 0x91, 0x3d, 0x8d, 0xbf, 0xff, 0xff,
-	0x00, 0x00, 0x00, 0xff, 0xff, 0xdc, 0x3d, 0x6f, 0x23, 0x50, 0x23, 0x00, 0x00,
+	// 2152 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x5a, 0xcd, 0x6f, 0x1b, 0xc7,
+	0xd9, 0x37, 0x25, 0xea, 0x83, 0x0f, 0xa9, 0x0f, 0xaf, 0x95, 0x88, 0xa1, 0x5e, 0x53, 0x34, 0xf3,
+	0x22, 0x91, 0xbf, 0x44, 0x47, 0x75, 0x8d, 0x54, 0x87, 0xa0, 0x92, 0xd3, 0x18, 0x42, 0xa2, 0x5a,
+	0x59, 0xda, 0x2e, 0x10, 0xa0, 0x60, 0x86, 0xbb, 0xe3, 0xd5, 0x36, 0xdc, 0x8f, 0xce, 0x2c, 0x65,
+	0x33, 0x05, 0x7a, 0xe8, 0xa5, 0xd7, 0x00, 0xe9, 0xa1, 0x87, 0xa2, 0x40, 0x81, 0x1e, 0x7a, 0x2a,
+	0x8a, 0x22, 0xb7, 0x02, 0x45, 0x2f, 0x05, 0x8c, 0x9e, 0x8c, 0x9c, 0x8a, 0x1e, 0xdc, 0xc0, 0x2e,
+	0xd0, 0x3f, 0x21, 0xd7, 0x62, 0x76, 0x66, 0x77, 0x87, 0xfb, 0x49, 0xcb, 0x52, 0xe2, 0x93, 0xbd,
+	0x33, 0xbf, 0x99, 0x79, 0x9e, 0xdf, 0xf3, 0x35, 0xf3, 0x50, 0xb0, 0x66, 0x10, 0x8c, 0xed, 0xfb,
+	0x26, 0x1e, 0xe8, 0x1d, 0xea, 0x39, 0x04, 0x19, 0xb8, 0xe3, 0x3d, 0xdc, 0x74, 0x89, 0xe3, 0x39,
+	0x8a, 0x12, 0x4d, 0x6e, 0x8a, 0xc9, 0xc6, 0xaa, 0xe6, 0x50, 0xcb, 0xa1, 0x1d, 0x8b, 0x1a, 0x9d,
+	0xa3, 0xb7, 0xd8, 0x3f, 0x1c, 0xdc, 0x78, 0x8d, 0x4f, 0xf4, 0xfc, 0xaf, 0x0e, 0xff, 0x10, 0x53,
+	0x2b, 0x86, 0x63, 0x38, 0x7c, 0x9c, 0xfd, 0x4f, 0x8c, 0xae, 0x1b, 0x8e, 0x63, 0x0c, 0x70, 0xc7,
+	0xff, 0xea, 0x0f, 0xef, 0x77, 0x3c, 0xd3, 0xc2, 0xd4, 0x43, 0x96, 0x2b, 0x00, 0x2d, 0x49, 0x36,
+	0xcd, 0xb1, 0x2c, 0xc7, 0xee, 0x20, 0xd7, 0x25, 0xce, 0x11, 0x1a, 0x84, 0x5b, 0x24, 0x10, 0x0f,
+	0x08, 0x72, 0x5d, 0x4c, 0x04, 0xa0, 0x2d, 0x01, 0x5c, 0x4c, 0x2c, 0x93, 0x52, 0xd3, 0xb1, 0x05,
+	0x36, 0x65, 0x93, 0x80, 0x82, 0x42, 0x80, 0x8b, 0x08, 0xb2, 0x84, 0x7e, 0xed, 0xbf, 0x4e, 0xc3,
+	0xd2, 0x3e, 0x35, 0x6e, 0x12, 0x8c, 0x3c, 0xbc, 0x3b, 0xd4, 0x3e, 0xc1, 0x9e, 0xb2, 0x05, 0x73,
+	0x1a, 0xfb, 0x76, 0x48, 0xbd, 0xd4, 0x2a, 0x6d, 0x54, 0x76, 0xeb, 0x5f, 0x7e, 0x71, 0x75, 0x45,
+	0xd0, 0xb2, 0xa3, 0xeb, 0x04, 0x53, 0xda, 0xf5, 0x88, 0x69, 0x1b, 0x6a, 0x00, 0x54, 0xd6, 0xa1,
+	0xda, 0xf7, 0x57, 0xf7, 0x6c, 0x64, 0xe1, 0xfa, 0x14, 0x5b, 0xa7, 0x02, 0x1f, 0xfa, 0x21, 0xb2,
+	0xb0, 0xb2, 0x0b, 0x70, 0x64, 0x52, 0xb3, 0x6f, 0x0e, 0x4c, 0x6f, 0x54, 0x9f, 0x6e, 0x95, 0x36,
+	0x16, 0xb7, 0xda, 0x9b, 0x49, 0x2b, 0x6d, 0xde, 0x0b, 0x51, 0x77, 0x46, 0x2e, 0x56, 0xa5, 0x55,
+	0xca, 0x0e, 0x2c, 0xb9, 0x68, 0x64, 0x61, 0xdb, 0xeb, 0x21, 0x2e, 0x46, 0xbd, 0x5c, 0x20, 0xe0,
+	0xa2, 0x58, 0x20, 0x46, 0x95, 0xf7, 0x40, 0x71, 0x89, 0x69, 0x21, 0x32, 0xea, 0x51, 0x37, 0xdc,
+	0x65, 0xa6, 0x60, 0x97, 0x65, 0xb1, 0xa6, 0xeb, 0x06, 0xfb, 0xbc, 0x0f, 0xe7, 0xe4, 0x7d, 0x84,
+	0x6d, 0xeb, 0xb3, 0xad, 0xd2, 0x46, 0x75, 0x6b, 0x4d, 0xd6, 0x4b, 0xd8, 0x63, 0x47, 0x40, 0xd4,
+	0xb3, 0xd1, 0x5e, 0x62, 0x48, 0xb9, 0x02, 0x8a, 0x76, 0x88, 0x88, 0x81, 0xf5, 0x1e, 0xc1, 0x48,
+	0xef, 0xfd, 0x74, 0xe8, 0x78, 0xa8, 0x3e, 0xd7, 0x2a, 0x6d, 0x94, 0xd5, 0x65, 0x31, 0xa3, 0x62,
+	0xa4, 0x7f, 0xc8, 0xc6, 0xb7, 0x6b, 0xbf, 0xf8, 0xef, 0x9f, 0x2e, 0x05, 0xc4, 0xb7, 0xbb, 0xb0,
+	0x1a, 0xb3, 0x9f, 0x8a, 0xa9, 0xeb, 0xd8, 0x14, 0x2b, 0x6f, 0x43, 0x45, 0xd8, 0xc4, 0xd4, 0x85,
+	0x25, 0xd7, 0x1e, 0x3d, 0x59, 0x3f, 0xf3, 0xaf, 0x27, 0xeb, 0xe5, 0xbb, 0xa6, 0xed, 0x7d, 0xf9,
+	0xc5, 0xd5, 0xaa, 0x50, 0x97, 0x7d, 0xaa, 0xf3, 0x1c, 0xbd, 0xa7, 0xb7, 0x1f, 0xf8, 0x4e, 0xf1,
+	0x2e, 0x1e, 0xe0, 0xd0, 0x29, 0xae, 0xc3, 0xbc, 0xe3, 0x62, 0x32, 0x91, 0x57, 0x84, 0xc8, 0x42,
+	0xb7, 0xd8, 0x5e, 0x60, 0xca, 0x84, 0xf8, 0xf6, 0x6b, 0xbe, 0x36, 0xf2, 0xc1, 0x81, 0x36, 0xed,
+	0x5f, 0x95, 0x60, 0x85, 0xcd, 0x99, 0x54, 0x73, 0x6c, 0xcf, 0xb4, 0x87, 0xa7, 0x2b, 0x99, 0xf2,
+	0x2a, 0xcc, 0x12, 0x8c, 0xa8, 0x63, 0xfb, 0xce, 0x5a, 0x51, 0xc5, 0x57, 0x5c, 0xe2, 0x26, 0xfc,
+	0x5f, 0x9a, 0x54, 0xa1, 0xd8, 0xff, 0x91, 0x03, 0xec, 0x76, 0xff, 0x27, 0x58, 0x3b, 0xa5, 0x00,
+	0x5b, 0x87, 0xaa, 0xe3, 0x6f, 0xcf, 0x01, 0x5c, 0x68, 0xe0, 0x43, 0x3e, 0xe0, 0x02, 0xd4, 0x5c,
+	0x34, 0x1a, 0x38, 0x48, 0xef, 0x51, 0xf3, 0x53, 0xec, 0x87, 0x4e, 0x59, 0xad, 0x8a, 0xb1, 0xae,
+	0xf9, 0x69, 0x3c, 0x48, 0x67, 0x8e, 0x15, 0xa4, 0x17, 0xa0, 0xc6, 0xa8, 0x60, 0x41, 0xea, 0x8d,
+	0x5c, 0xec, 0x87, 0x44, 0x45, 0xad, 0x8a, 0x31, 0x06, 0xcf, 0x0a, 0x9e, 0xb9, 0x63, 0x05, 0xcf,
+	0x45, 0x58, 0xc6, 0x0f, 0x5d, 0xa6, 0xb7, 0x76, 0x88, 0xb5, 0x4f, 0xe8, 0xd0, 0xa2, 0xf5, 0xf9,
+	0xd6, 0xf4, 0x46, 0x4d, 0x5d, 0xe2, 0xe3, 0x37, 0x83, 0x61, 0xe5, 0x7d, 0x58, 0x22, 0x58, 0x1f,
+	0xda, 0x3a, 0xb2, 0xb5, 0x11, 0x97, 0xae, 0x92, 0xad, 0xa3, 0x1a, 0x42, 0x7d, 0x1d, 0x17, 0xc9,
+	0xd8, 0x77, 0x4e, 0x18, 0x72, 0x2b, 0xcb, 0x61, 0x28, 0x0c, 0x33, 0x61, 0x18, 0x72, 0xf4, 0x9e,
+	0xde, 0xfe, 0x7c, 0x0a, 0x16, 0xf6, 0xa9, 0xd1, 0xc5, 0x68, 0x20, 0x3c, 0xe7, 0x94, 0x7c, 0xbd,
+	0xd0, 0x77, 0xbe, 0x0b, 0xab, 0xc6, 0xc0, 0xe9, 0xa3, 0x41, 0xef, 0xc8, 0x24, 0xde, 0x10, 0x0d,
+	0x7a, 0x06, 0x71, 0x86, 0x2e, 0xd3, 0x88, 0xb9, 0xd1, 0x82, 0xba, 0xc2, 0xa7, 0xef, 0xf1, 0xd9,
+	0x5b, 0x6c, 0x72, 0x4f, 0x57, 0xde, 0x85, 0x75, 0x8a, 0x35, 0xc7, 0xd6, 0x85, 0xa9, 0xfb, 0x03,
+	0xda, 0x43, 0x86, 0xd1, 0xa3, 0xa6, 0x61, 0x23, 0x6f, 0x48, 0x30, 0x4f, 0xbd, 0x35, 0x75, 0x2d,
+	0x84, 0x75, 0xdd, 0xdd, 0x01, 0xdd, 0x31, 0x8c, 0x6e, 0x08, 0x89, 0x47, 0xdc, 0x2a, 0xbc, 0x32,
+	0x46, 0x4a, 0x18, 0x6a, 0xbf, 0x29, 0xc1, 0xb9, 0x7d, 0x6a, 0xa8, 0x98, 0x8d, 0x7e, 0xfb, 0xa4,
+	0xc5, 0xe5, 0x3e, 0x0f, 0x6b, 0x29, 0xd2, 0x85, 0xd2, 0xff, 0x91, 0x1b, 0xfb, 0xa6, 0xe3, 0x8e,
+	0x84, 0xdc, 0x8d, 0xb8, 0xdc, 0x92, 0x74, 0x6f, 0xc0, 0x12, 0x25, 0x5a, 0x2f, 0x29, 0xe1, 0x02,
+	0x25, 0xda, 0x6e, 0x24, 0xe4, 0x1b, 0xb0, 0xa4, 0x53, 0x6f, 0x0c, 0xc7, 0x05, 0x5d, 0xd0, 0xa9,
+	0x37, 0x8e, 0x63, 0xfb, 0xc9, 0x0a, 0x95, 0xc3, 0xfd, 0x6e, 0x47, 0x8e, 0x20, 0xf6, 0x93, 0x71,
+	0x33, 0xe1, 0x7e, 0x12, 0x4e, 0x85, 0x55, 0x86, 0x3b, 0x66, 0x8d, 0x5c, 0xd1, 0xa9, 0x77, 0x10,
+	0x8f, 0xf4, 0x38, 0x9f, 0x1f, 0xfa, 0x7e, 0x10, 0xf1, 0x75, 0x02, 0x01, 0xf7, 0xeb, 0x92, 0x54,
+	0xf8, 0x5e, 0x2e, 0xef, 0x91, 0x2b, 0x63, 0xcc, 0x73, 0x1e, 0x27, 0x2a, 0xe3, 0xe9, 0x8a, 0xbe,
+	0x0d, 0x10, 0xf2, 0x4b, 0xeb, 0xd3, 0xad, 0xe9, 0x22, 0x82, 0x2b, 0x01, 0xc1, 0x54, 0xaa, 0xaa,
+	0xe5, 0xe7, 0xaa, 0xaa, 0x31, 0x95, 0xff, 0x52, 0x82, 0xc5, 0x30, 0xdf, 0xfa, 0xd9, 0xe6, 0x58,
+	0x45, 0xf5, 0x3c, 0x00, 0xcf, 0x63, 0x92, 0xa6, 0x15, 0x7f, 0xc4, 0x57, 0x74, 0x0b, 0xe6, 0x2c,
+	0x6c, 0xf5, 0x31, 0x09, 0xb4, 0xcc, 0xd9, 0x52, 0x00, 0x95, 0x15, 0x98, 0xc1, 0x0f, 0x3d, 0x82,
+	0x84, 0x7e, 0xfc, 0x23, 0x56, 0x2c, 0x0e, 0xe0, 0xd5, 0x71, 0xe1, 0x43, 0xd7, 0xbd, 0x01, 0xf3,
+	0x61, 0x62, 0x9d, 0xc0, 0x73, 0xe7, 0x0c, 0x9e, 0x68, 0xdb, 0x9e, 0x4f, 0x07, 0xf7, 0x0e, 0x4e,
+	0xc7, 0xf1, 0x6c, 0x9f, 0x4f, 0x48, 0xdc, 0x4a, 0x75, 0x5f, 0x0f, 0xe9, 0xd4, 0xc8, 0x3e, 0x53,
+	0xbe, 0x4b, 0xde, 0x75, 0xf5, 0x40, 0xc5, 0x7d, 0x9f, 0x9f, 0x63, 0x8a, 0xf5, 0x3d, 0xa8, 0x72,
+	0xb1, 0x9c, 0x07, 0x36, 0x26, 0x5c, 0xae, 0x9c, 0x85, 0x5c, 0x87, 0xdb, 0x0c, 0x1b, 0xd3, 0x68,
+	0x3a, 0x6e, 0xe2, 0x77, 0x60, 0x51, 0x58, 0xae, 0xe7, 0x39, 0xec, 0x3d, 0x50, 0x2f, 0x17, 0x58,
+	0xba, 0x26, 0xf0, 0x77, 0x9c, 0x1d, 0x9d, 0x55, 0xb8, 0xb3, 0xd2, 0x7a, 0xdd, 0xa7, 0xa2, 0x3e,
+	0x53, 0xb0, 0xc5, 0x52, 0xb8, 0x05, 0xe7, 0x2e, 0xdd, 0xfb, 0x13, 0xe4, 0x85, 0xec, 0xfe, 0x83,
+	0x17, 0x3a, 0x09, 0xf0, 0x03, 0xe6, 0x65, 0x2f, 0x1d, 0xb9, 0xe9, 0xb1, 0x90, 0x5a, 0x16, 0xe3,
+	0xba, 0x84, 0xba, 0xfe, 0xa1, 0xe4, 0x97, 0xc5, 0x0f, 0x30, 0x3a, 0x12, 0x9e, 0x7d, 0x0d, 0x66,
+	0x39, 0x7f, 0x85, 0x3a, 0x0a, 0xdc, 0xe9, 0x69, 0xb8, 0x5d, 0x65, 0xba, 0x88, 0x63, 0xc4, 0xc5,
+	0x24, 0x92, 0x34, 0x4a, 0xd0, 0x53, 0x92, 0xbd, 0x78, 0xd1, 0xdd, 0xb3, 0xef, 0x3b, 0xa7, 0x95,
+	0x9f, 0x3f, 0x48, 0x7d, 0x4e, 0x4e, 0xfb, 0x65, 0xb7, 0x99, 0x52, 0x76, 0xef, 0xee, 0xd9, 0xde,
+	0x8d, 0xeb, 0xf7, 0xd0, 0x60, 0x88, 0x93, 0xcf, 0xcd, 0x93, 0x78, 0x74, 0x9f, 0xc0, 0xb3, 0x22,
+	0xcf, 0x6b, 0x22, 0x46, 0x43, 0xc6, 0x7f, 0x5b, 0xe2, 0x97, 0x03, 0x64, 0x6b, 0x78, 0x30, 0xf6,
+	0xf6, 0x7a, 0x49, 0xca, 0xf9, 0x3a, 0x9c, 0x4f, 0x95, 0x2f, 0xd4, 0xe0, 0x6f, 0x53, 0x50, 0xdb,
+	0xa7, 0xc6, 0xc1, 0xd0, 0x3b, 0x70, 0x06, 0xa6, 0x36, 0x3a, 0xa6, 0xe0, 0xef, 0x40, 0xc5, 0x25,
+	0xa6, 0xad, 0x99, 0x2e, 0x1a, 0xf8, 0x62, 0x57, 0xb7, 0x5a, 0x32, 0xf3, 0x51, 0x67, 0x69, 0xf3,
+	0x20, 0xc0, 0xa9, 0xd1, 0x12, 0x76, 0x07, 0x25, 0x98, 0x3a, 0x43, 0xa2, 0x05, 0x4a, 0x85, 0xdf,
+	0xca, 0xf7, 0x01, 0xa8, 0x87, 0x3c, 0xcc, 0x4c, 0x4d, 0xfd, 0xbc, 0x99, 0xbd, 0x79, 0x37, 0x00,
+	0xaa, 0xd2, 0x1a, 0x65, 0x1f, 0xd8, 0x1b, 0xcd, 0x24, 0xc8, 0x33, 0x1d, 0xbb, 0xe7, 0x99, 0x16,
+	0x16, 0x8f, 0xc0, 0xc6, 0x26, 0xef, 0xb0, 0x6d, 0x06, 0x1d, 0xb6, 0xcd, 0x3b, 0x41, 0x87, 0x6d,
+	0x77, 0xfe, 0xd1, 0x93, 0xf5, 0xd2, 0x67, 0xff, 0x5e, 0x2f, 0xa9, 0x8b, 0xd1, 0x62, 0x36, 0x1d,
+	0xe7, 0xf8, 0xc0, 0xaf, 0x41, 0x21, 0x83, 0xf2, 0xfd, 0xd0, 0xf5, 0x47, 0x82, 0xe7, 0x4b, 0xd1,
+	0xfd, 0x90, 0xa3, 0xf7, 0xf4, 0xf6, 0x9f, 0xe5, 0xfb, 0xe1, 0xcb, 0x6a, 0x97, 0x38, 0x0d, 0x5d,
+	0xe9, 0xe6, 0x78, 0x62, 0x4c, 0xfc, 0x9d, 0x33, 0xb1, 0x6f, 0x12, 0xe2, 0x90, 0x17, 0x0a, 0xad,
+	0xcb, 0x30, 0x65, 0xea, 0x22, 0x27, 0xe7, 0x1e, 0x3e, 0x65, 0xea, 0xf1, 0x38, 0x9c, 0x2e, 0x8a,
+	0xc3, 0xf2, 0x64, 0xd7, 0x6a, 0x59, 0x8d, 0x30, 0x02, 0x7f, 0x2f, 0xab, 0xf8, 0x42, 0xbd, 0xa6,
+	0x13, 0x55, 0x31, 0x2f, 0x13, 0x72, 0x0d, 0xc6, 0x32, 0xe1, 0x57, 0xf2, 0x5d, 0x21, 0x9a, 0xff,
+	0xd6, 0x3a, 0x09, 0xe3, 0xb5, 0xa0, 0x7c, 0x12, 0xb5, 0x40, 0xb6, 0x61, 0xac, 0xfb, 0xf6, 0x3b,
+	0xfe, 0x4e, 0xe0, 0x73, 0x2f, 0x72, 0x31, 0x7e, 0x2e, 0x13, 0x16, 0x5c, 0x1a, 0x52, 0x6f, 0xd1,
+	0x92, 0x88, 0xa1, 0xf4, 0x9f, 0x73, 0x0f, 0xe4, 0xb6, 0x3b, 0xf0, 0xdb, 0xf6, 0xca, 0x0d, 0xa8,
+	0xa0, 0xa1, 0x77, 0xe8, 0x10, 0x46, 0x5f, 0x91, 0xfc, 0x11, 0x54, 0x79, 0x1b, 0x66, 0x79, 0xe3,
+	0x5f, 0x64, 0x9b, 0x46, 0x1a, 0xe7, 0xfc, 0x8c, 0xdd, 0x32, 0x53, 0x50, 0x15, 0xf8, 0xed, 0x45,
+	0x26, 0x6e, 0xb4, 0x93, 0xa0, 0x5b, 0x16, 0x2a, 0x14, 0xf8, 0xeb, 0x12, 0x2c, 0xfb, 0xba, 0x18,
+	0x04, 0x9d, 0x72, 0xe7, 0x58, 0xb9, 0x08, 0x67, 0x63, 0x1d, 0x06, 0x53, 0xf7, 0xb9, 0x5e, 0x50,
+	0x17, 0xe5, 0xf6, 0xc1, 0x9e, 0x9e, 0xd7, 0x8c, 0x28, 0x9f, 0x50, 0x33, 0xa2, 0x01, 0xf5, 0xb8,
+	0xe2, 0x21, 0x2b, 0xbf, 0x9c, 0xf2, 0x27, 0x6f, 0x3a, 0x96, 0xcb, 0x72, 0xf0, 0x37, 0xc2, 0xce,
+	0x2e, 0x34, 0x53, 0x1b, 0x76, 0xf7, 0x91, 0x65, 0x0e, 0x46, 0x11, 0x55, 0x8d, 0x64, 0xdf, 0xee,
+	0x3d, 0x1f, 0xb2, 0xa7, 0x2b, 0x3b, 0x50, 0x33, 0x8e, 0x8c, 0x9e, 0x85, 0x5c, 0xd7, 0xb4, 0x8d,
+	0xa0, 0xc2, 0x37, 0xd3, 0x1c, 0xe7, 0xd6, 0xbd, 0x5b, 0xfb, 0x1c, 0xa6, 0x56, 0x8d, 0x23, 0x43,
+	0xfc, 0x3f, 0xd1, 0xba, 0x6b, 0x43, 0x2b, 0x8b, 0x88, 0x90, 0xad, 0x9f, 0xf3, 0xc7, 0xb1, 0x7f,
+	0x33, 0xfa, 0x26, 0xa8, 0x8a, 0xcb, 0xd8, 0x82, 0x66, 0xfa, 0xf9, 0x81, 0x84, 0x5b, 0x5f, 0xaf,
+	0xc0, 0xf4, 0x3e, 0x35, 0x94, 0x8f, 0xa1, 0x36, 0xf6, 0xbb, 0xd9, 0xeb, 0x69, 0xcc, 0xc4, 0x7e,
+	0x9c, 0x69, 0x5c, 0x9e, 0x00, 0x14, 0xd6, 0xe7, 0x8f, 0xa1, 0x36, 0xf6, 0x23, 0x4c, 0xd6, 0x09,
+	0x32, 0x28, 0xf3, 0x84, 0xb4, 0x5f, 0x55, 0x94, 0x01, 0x2c, 0x27, 0x9e, 0x25, 0x6f, 0x66, 0x6c,
+	0x10, 0x07, 0x36, 0x3a, 0x13, 0x02, 0x65, 0x7d, 0xc6, 0xca, 0x69, 0x96, 0x3e, 0x32, 0x28, 0x53,
+	0x9f, 0xb4, 0x84, 0xaf, 0x38, 0x70, 0x36, 0xf9, 0x0b, 0xd1, 0x46, 0x16, 0x23, 0x71, 0x64, 0xe3,
+	0xda, 0xa4, 0x48, 0x59, 0xa5, 0xb1, 0xf7, 0x45, 0xbe, 0x13, 0x70, 0x50, 0x81, 0x13, 0xc4, 0xda,
+	0x99, 0x1f, 0x01, 0x48, 0xcd, 0xec, 0x0b, 0x19, 0x4b, 0x23, 0x48, 0xe3, 0x62, 0x21, 0x44, 0x36,
+	0x7f, 0xa2, 0x5d, 0x9e, 0x65, 0xfe, 0x38, 0x30, 0xd3, 0xfc, 0x59, 0x2d, 0x6e, 0xa6, 0x89, 0xd4,
+	0xde, 0xce, 0xd2, 0x24, 0x82, 0x64, 0x6a, 0x92, 0xd2, 0xf4, 0x0d, 0x43, 0xa5, 0xc0, 0x0e, 0x32,
+	0xa8, 0x20, 0x54, 0x62, 0x27, 0x10, 0x50, 0x52, 0xde, 0x93, 0x99, 0x22, 0x26, 0xa0, 0x8d, 0xb7,
+	0x26, 0x86, 0x26, 0x03, 0xa6, 0x40, 0x2b, 0x19, 0x54, 0x10, 0x30, 0xb1, 0x13, 0xc6, 0x03, 0x46,
+	0x1c, 0x33, 0x41, 0xc0, 0x88, 0xb3, 0xae, 0x4d, 0x8a, 0x4c, 0x66, 0x1c, 0xe9, 0x32, 0x9a, 0x9f,
+	0x71, 0x22, 0x60, 0x41, 0xc6, 0x49, 0x5e, 0x7f, 0x95, 0x1f, 0x43, 0x55, 0x6e, 0x12, 0xb7, 0x73,
+	0x03, 0xcf, 0xc7, 0x34, 0x2e, 0x15, 0x63, 0xe4, 0xed, 0xe5, 0xa6, 0x6b, 0x3b, 0xd7, 0x9f, 0xf2,
+	0xb7, 0x4f, 0x69, 0xa3, 0x32, 0xe3, 0x24, 0x5b, 0xa8, 0x1b, 0xb9, 0x1c, 0x48, 0xc8, 0x4c, 0xe3,
+	0x64, 0x76, 0x16, 0x23, 0xe3, 0x48, 0x5d, 0xc5, 0x37, 0x8b, 0x77, 0xf1, 0x81, 0x05, 0xc6, 0x49,
+	0xf6, 0xf6, 0x58, 0x3e, 0x90, 0xfa, 0x7a, 0x59, 0xf9, 0x20, 0x82, 0x64, 0xe6, 0x83, 0x64, 0xcf,
+	0x8d, 0x59, 0x46, 0xbe, 0xf5, 0xb7, 0x73, 0x63, 0x22, 0xdf, 0x32, 0x29, 0x57, 0x73, 0xe5, 0x47,
+	0x50, 0x89, 0x5a, 0x33, 0xad, 0x8c, 0x85, 0x21, 0xa2, 0xb1, 0x51, 0x84, 0x48, 0xe6, 0x31, 0xb1,
+	0x77, 0x7e, 0x1e, 0x13, 0xdb, 0x5f, 0x9e, 0x00, 0x24, 0x9f, 0x30, 0xf6, 0xa2, 0x78, 0x3d, 0xd7,
+	0x6c, 0x1c, 0x94, 0x79, 0x42, 0xda, 0x33, 0x40, 0xd1, 0x60, 0x61, 0xfc, 0xe6, 0xf6, 0xff, 0x99,
+	0xcc, 0x4a, 0xa8, 0xc6, 0x95, 0x49, 0x50, 0xe1, 0x21, 0x3f, 0x83, 0x57, 0xd2, 0x6f, 0xd4, 0x57,
+	0x32, 0x8b, 0x46, 0x0a, 0xba, 0x71, 0xfd, 0x79, 0xd0, 0xe1, 0xe1, 0x43, 0x38, 0x97, 0x76, 0x43,
+	0xbd, 0x94, 0x9b, 0xe1, 0xc7, 0x0f, 0xde, 0x9a, 0x1c, 0x1b, 0x1c, 0xbb, 0xbb, 0xf7, 0xe8, 0x69,
+	0xb3, 0xf4, 0xf8, 0x69, 0xb3, 0xf4, 0xd5, 0xd3, 0x66, 0xe9, 0xb3, 0x67, 0xcd, 0x33, 0x8f, 0x9f,
+	0x35, 0xcf, 0xfc, 0xf3, 0x59, 0xf3, 0xcc, 0x47, 0x1d, 0xc3, 0xf4, 0x0e, 0x87, 0x7d, 0xf6, 0x78,
+	0xe9, 0xf4, 0xed, 0xfe, 0x55, 0xed, 0x10, 0x99, 0x76, 0x47, 0xfa, 0xeb, 0xaf, 0x87, 0xd1, 0xdf,
+	0xc8, 0x8d, 0x5c, 0x4c, 0xfb, 0xb3, 0x7e, 0x67, 0xed, 0x3b, 0xff, 0x0b, 0x00, 0x00, 0xff, 0xff,
+	0x66, 0x33, 0x60, 0x9d, 0x46, 0x27, 0x00, 0x00,
 }
 
 // Reference imports to suppress errors if they are not otherwise used.
@@ -2737,11 +3054,12 @@ type MsgClient interface {
 	// basic operation of policy
 	PutPolicy(ctx context.Context, in *MsgPutPolicy, opts ...grpc.CallOption) (*MsgPutPolicyResponse, error)
 	DeletePolicy(ctx context.Context, in *MsgDeletePolicy, opts ...grpc.CallOption) (*MsgDeletePolicyResponse, error)
-	// UpdateParams defines a governance operation for updating the x/storage module parameters.
-	// The authority is defined in the keeper.
-	//
 	// Since: cosmos-sdk 0.47
 	UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error)
+	// this line is used by starport scaffolding # proto/tx/rpc
+	MigrateBucket(ctx context.Context, in *MsgMigrateBucket, opts ...grpc.CallOption) (*MsgMigrateBucketResponse, error)
+	CompleteMigrateBucket(ctx context.Context, in *MsgCompleteMigrateBucket, opts ...grpc.CallOption) (*MsgCompleteMigrateBucketResponse, error)
+	CancelMigrateBucket(ctx context.Context, in *MsgCancelMigrateBucket, opts ...grpc.CallOption) (*MsgCancelMigrateBucketResponse, error)
 }
 
 type msgClient struct {
@@ -2959,6 +3277,33 @@ func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts
 	return out, nil
 }
 
+func (c *msgClient) MigrateBucket(ctx context.Context, in *MsgMigrateBucket, opts ...grpc.CallOption) (*MsgMigrateBucketResponse, error) {
+	out := new(MsgMigrateBucketResponse)
+	err := c.cc.Invoke(ctx, "/greenfield.storage.Msg/MigrateBucket", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *msgClient) CompleteMigrateBucket(ctx context.Context, in *MsgCompleteMigrateBucket, opts ...grpc.CallOption) (*MsgCompleteMigrateBucketResponse, error) {
+	out := new(MsgCompleteMigrateBucketResponse)
+	err := c.cc.Invoke(ctx, "/greenfield.storage.Msg/CompleteMigrateBucket", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *msgClient) CancelMigrateBucket(ctx context.Context, in *MsgCancelMigrateBucket, opts ...grpc.CallOption) (*MsgCancelMigrateBucketResponse, error) {
+	out := new(MsgCancelMigrateBucketResponse)
+	err := c.cc.Invoke(ctx, "/greenfield.storage.Msg/CancelMigrateBucket", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
 // MsgServer is the server API for Msg service.
 type MsgServer interface {
 	// basic operation of bucket
@@ -2987,11 +3332,12 @@ type MsgServer interface {
 	// basic operation of policy
 	PutPolicy(context.Context, *MsgPutPolicy) (*MsgPutPolicyResponse, error)
 	DeletePolicy(context.Context, *MsgDeletePolicy) (*MsgDeletePolicyResponse, error)
-	// UpdateParams defines a governance operation for updating the x/storage module parameters.
-	// The authority is defined in the keeper.
-	//
 	// Since: cosmos-sdk 0.47
 	UpdateParams(context.Context, *MsgUpdateParams) (*MsgUpdateParamsResponse, error)
+	// this line is used by starport scaffolding # proto/tx/rpc
+	MigrateBucket(context.Context, *MsgMigrateBucket) (*MsgMigrateBucketResponse, error)
+	CompleteMigrateBucket(context.Context, *MsgCompleteMigrateBucket) (*MsgCompleteMigrateBucketResponse, error)
+	CancelMigrateBucket(context.Context, *MsgCancelMigrateBucket) (*MsgCancelMigrateBucketResponse, error)
 }
 
 // UnimplementedMsgServer can be embedded to have forward compatible implementations.
@@ -3067,6 +3413,15 @@ func (*UnimplementedMsgServer) DeletePolicy(ctx context.Context, req *MsgDeleteP
 func (*UnimplementedMsgServer) UpdateParams(ctx context.Context, req *MsgUpdateParams) (*MsgUpdateParamsResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method UpdateParams not implemented")
 }
+func (*UnimplementedMsgServer) MigrateBucket(ctx context.Context, req *MsgMigrateBucket) (*MsgMigrateBucketResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method MigrateBucket not implemented")
+}
+func (*UnimplementedMsgServer) CompleteMigrateBucket(ctx context.Context, req *MsgCompleteMigrateBucket) (*MsgCompleteMigrateBucketResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method CompleteMigrateBucket not implemented")
+}
+func (*UnimplementedMsgServer) CancelMigrateBucket(ctx context.Context, req *MsgCancelMigrateBucket) (*MsgCancelMigrateBucketResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method CancelMigrateBucket not implemented")
+}
 
 func RegisterMsgServer(s grpc1.Server, srv MsgServer) {
 	s.RegisterService(&_Msg_serviceDesc, srv)
@@ -3486,6 +3841,60 @@ func _Msg_UpdateParams_Handler(srv interface{}, ctx context.Context, dec func(in
 	return interceptor(ctx, in, info, handler)
 }
 
+func _Msg_MigrateBucket_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(MsgMigrateBucket)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(MsgServer).MigrateBucket(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/greenfield.storage.Msg/MigrateBucket",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(MsgServer).MigrateBucket(ctx, req.(*MsgMigrateBucket))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Msg_CompleteMigrateBucket_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(MsgCompleteMigrateBucket)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(MsgServer).CompleteMigrateBucket(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/greenfield.storage.Msg/CompleteMigrateBucket",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(MsgServer).CompleteMigrateBucket(ctx, req.(*MsgCompleteMigrateBucket))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Msg_CancelMigrateBucket_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(MsgCancelMigrateBucket)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(MsgServer).CancelMigrateBucket(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/greenfield.storage.Msg/CancelMigrateBucket",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(MsgServer).CancelMigrateBucket(ctx, req.(*MsgCancelMigrateBucket))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
 var _Msg_serviceDesc = grpc.ServiceDesc{
 	ServiceName: "greenfield.storage.Msg",
 	HandlerType: (*MsgServer)(nil),
@@ -3582,6 +3991,18 @@ var _Msg_serviceDesc = grpc.ServiceDesc{
 			MethodName: "UpdateParams",
 			Handler:    _Msg_UpdateParams_Handler,
 		},
+		{
+			MethodName: "MigrateBucket",
+			Handler:    _Msg_MigrateBucket_Handler,
+		},
+		{
+			MethodName: "CompleteMigrateBucket",
+			Handler:    _Msg_CompleteMigrateBucket_Handler,
+		},
+		{
+			MethodName: "CancelMigrateBucket",
+			Handler:    _Msg_CancelMigrateBucket_Handler,
+		},
 	},
 	Streams:  []grpc.StreamDesc{},
 	Metadata: "greenfield/storage/tx.proto",
@@ -3610,7 +4031,7 @@ func (m *MsgCreateBucket) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	if m.ChargedReadQuota != 0 {
 		i = encodeVarintTx(dAtA, i, uint64(m.ChargedReadQuota))
 		i--
-		dAtA[i] = 0x40
+		dAtA[i] = 0x38
 	}
 	if m.PrimarySpApproval != nil {
 		{
@@ -3622,14 +4043,14 @@ func (m *MsgCreateBucket) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 			i = encodeVarintTx(dAtA, i, uint64(size))
 		}
 		i--
-		dAtA[i] = 0x3a
+		dAtA[i] = 0x32
 	}
 	if len(m.PrimarySpAddress) > 0 {
 		i -= len(m.PrimarySpAddress)
 		copy(dAtA[i:], m.PrimarySpAddress)
 		i = encodeVarintTx(dAtA, i, uint64(len(m.PrimarySpAddress)))
 		i--
-		dAtA[i] = 0x32
+		dAtA[i] = 0x2a
 	}
 	if len(m.PaymentAddress) > 0 {
 		i -= len(m.PaymentAddress)
@@ -3840,15 +4261,6 @@ func (m *MsgCreateObject) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	_ = i
 	var l int
 	_ = l
-	if len(m.ExpectSecondarySpAddresses) > 0 {
-		for iNdEx := len(m.ExpectSecondarySpAddresses) - 1; iNdEx >= 0; iNdEx-- {
-			i -= len(m.ExpectSecondarySpAddresses[iNdEx])
-			copy(dAtA[i:], m.ExpectSecondarySpAddresses[iNdEx])
-			i = encodeVarintTx(dAtA, i, uint64(len(m.ExpectSecondarySpAddresses[iNdEx])))
-			i--
-			dAtA[i] = 0x52
-		}
-	}
 	if m.RedundancyType != 0 {
 		i = encodeVarintTx(dAtA, i, uint64(m.RedundancyType))
 		i--
@@ -3969,23 +4381,17 @@ func (m *MsgSealObject) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	_ = i
 	var l int
 	_ = l
-	if len(m.SecondarySpSignatures) > 0 {
-		for iNdEx := len(m.SecondarySpSignatures) - 1; iNdEx >= 0; iNdEx-- {
-			i -= len(m.SecondarySpSignatures[iNdEx])
-			copy(dAtA[i:], m.SecondarySpSignatures[iNdEx])
-			i = encodeVarintTx(dAtA, i, uint64(len(m.SecondarySpSignatures[iNdEx])))
-			i--
-			dAtA[i] = 0x2a
-		}
+	if len(m.SecondarySpBlsAggSignatures) > 0 {
+		i -= len(m.SecondarySpBlsAggSignatures)
+		copy(dAtA[i:], m.SecondarySpBlsAggSignatures)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.SecondarySpBlsAggSignatures)))
+		i--
+		dAtA[i] = 0x2a
 	}
-	if len(m.SecondarySpAddresses) > 0 {
-		for iNdEx := len(m.SecondarySpAddresses) - 1; iNdEx >= 0; iNdEx-- {
-			i -= len(m.SecondarySpAddresses[iNdEx])
-			copy(dAtA[i:], m.SecondarySpAddresses[iNdEx])
-			i = encodeVarintTx(dAtA, i, uint64(len(m.SecondarySpAddresses[iNdEx])))
-			i--
-			dAtA[i] = 0x22
-		}
+	if m.GlobalVirtualGroupId != 0 {
+		i = encodeVarintTx(dAtA, i, uint64(m.GlobalVirtualGroupId))
+		i--
+		dAtA[i] = 0x20
 	}
 	if len(m.ObjectName) > 0 {
 		i -= len(m.ObjectName)
@@ -5415,6 +5821,222 @@ func (m *MsgUpdateParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error)
 	return len(dAtA) - i, nil
 }
 
+func (m *MsgMigrateBucket) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MsgMigrateBucket) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgMigrateBucket) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.DstPrimarySpApproval != nil {
+		{
+			size, err := m.DstPrimarySpApproval.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintTx(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0x22
+	}
+	if m.DstPrimarySpId != 0 {
+		i = encodeVarintTx(dAtA, i, uint64(m.DstPrimarySpId))
+		i--
+		dAtA[i] = 0x18
+	}
+	if len(m.BucketName) > 0 {
+		i -= len(m.BucketName)
+		copy(dAtA[i:], m.BucketName)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.BucketName)))
+		i--
+		dAtA[i] = 0x12
+	}
+	if len(m.Operator) > 0 {
+		i -= len(m.Operator)
+		copy(dAtA[i:], m.Operator)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.Operator)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgMigrateBucketResponse) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MsgMigrateBucketResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgMigrateBucketResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgCompleteMigrateBucket) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MsgCompleteMigrateBucket) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgCompleteMigrateBucket) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.GvgMappings) > 0 {
+		for iNdEx := len(m.GvgMappings) - 1; iNdEx >= 0; iNdEx-- {
+			{
+				size, err := m.GvgMappings[iNdEx].MarshalToSizedBuffer(dAtA[:i])
+				if err != nil {
+					return 0, err
+				}
+				i -= size
+				i = encodeVarintTx(dAtA, i, uint64(size))
+			}
+			i--
+			dAtA[i] = 0x22
+		}
+	}
+	if m.GlobalVirtualGroupFamilyId != 0 {
+		i = encodeVarintTx(dAtA, i, uint64(m.GlobalVirtualGroupFamilyId))
+		i--
+		dAtA[i] = 0x18
+	}
+	if len(m.BucketName) > 0 {
+		i -= len(m.BucketName)
+		copy(dAtA[i:], m.BucketName)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.BucketName)))
+		i--
+		dAtA[i] = 0x12
+	}
+	if len(m.Operator) > 0 {
+		i -= len(m.Operator)
+		copy(dAtA[i:], m.Operator)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.Operator)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgCompleteMigrateBucketResponse) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MsgCompleteMigrateBucketResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgCompleteMigrateBucketResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgCancelMigrateBucket) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MsgCancelMigrateBucket) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgCancelMigrateBucket) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.BucketName) > 0 {
+		i -= len(m.BucketName)
+		copy(dAtA[i:], m.BucketName)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.BucketName)))
+		i--
+		dAtA[i] = 0x12
+	}
+	if len(m.Operator) > 0 {
+		i -= len(m.Operator)
+		copy(dAtA[i:], m.Operator)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.Operator)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgCancelMigrateBucketResponse) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MsgCancelMigrateBucketResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgCancelMigrateBucketResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	return len(dAtA) - i, nil
+}
+
 func encodeVarintTx(dAtA []byte, offset int, v uint64) int {
 	offset -= sovTx(v)
 	base := offset
@@ -5569,12 +6191,6 @@ func (m *MsgCreateObject) Size() (n int) {
 	if m.RedundancyType != 0 {
 		n += 1 + sovTx(uint64(m.RedundancyType))
 	}
-	if len(m.ExpectSecondarySpAddresses) > 0 {
-		for _, s := range m.ExpectSecondarySpAddresses {
-			l = len(s)
-			n += 1 + l + sovTx(uint64(l))
-		}
-	}
 	return n
 }
 
@@ -5607,17 +6223,12 @@ func (m *MsgSealObject) Size() (n int) {
 	if l > 0 {
 		n += 1 + l + sovTx(uint64(l))
 	}
-	if len(m.SecondarySpAddresses) > 0 {
-		for _, s := range m.SecondarySpAddresses {
-			l = len(s)
-			n += 1 + l + sovTx(uint64(l))
-		}
+	if m.GlobalVirtualGroupId != 0 {
+		n += 1 + sovTx(uint64(m.GlobalVirtualGroupId))
 	}
-	if len(m.SecondarySpSignatures) > 0 {
-		for _, b := range m.SecondarySpSignatures {
-			l = len(b)
-			n += 1 + l + sovTx(uint64(l))
-		}
+	l = len(m.SecondarySpBlsAggSignatures)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
 	}
 	return n
 }
@@ -6227,11 +6838,105 @@ func (m *MsgUpdateParamsResponse) Size() (n int) {
 	return n
 }
 
-func sovTx(x uint64) (n int) {
-	return (math_bits.Len64(x|1) + 6) / 7
+func (m *MsgMigrateBucket) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Operator)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	l = len(m.BucketName)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	if m.DstPrimarySpId != 0 {
+		n += 1 + sovTx(uint64(m.DstPrimarySpId))
+	}
+	if m.DstPrimarySpApproval != nil {
+		l = m.DstPrimarySpApproval.Size()
+		n += 1 + l + sovTx(uint64(l))
+	}
+	return n
 }
-func sozTx(x uint64) (n int) {
-	return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+
+func (m *MsgMigrateBucketResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	return n
+}
+
+func (m *MsgCompleteMigrateBucket) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Operator)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	l = len(m.BucketName)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	if m.GlobalVirtualGroupFamilyId != 0 {
+		n += 1 + sovTx(uint64(m.GlobalVirtualGroupFamilyId))
+	}
+	if len(m.GvgMappings) > 0 {
+		for _, e := range m.GvgMappings {
+			l = e.Size()
+			n += 1 + l + sovTx(uint64(l))
+		}
+	}
+	return n
+}
+
+func (m *MsgCompleteMigrateBucketResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	return n
+}
+
+func (m *MsgCancelMigrateBucket) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Operator)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	l = len(m.BucketName)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	return n
+}
+
+func (m *MsgCancelMigrateBucketResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	return n
+}
+
+func sovTx(x uint64) (n int) {
+	return (math_bits.Len64(x|1) + 6) / 7
+}
+func sozTx(x uint64) (n int) {
+	return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63))))
 }
 func (m *MsgCreateBucket) Unmarshal(dAtA []byte) error {
 	l := len(dAtA)
@@ -6377,7 +7082,7 @@ func (m *MsgCreateBucket) Unmarshal(dAtA []byte) error {
 			}
 			m.PaymentAddress = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
-		case 6:
+		case 5:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field PrimarySpAddress", wireType)
 			}
@@ -6409,7 +7114,7 @@ func (m *MsgCreateBucket) Unmarshal(dAtA []byte) error {
 			}
 			m.PrimarySpAddress = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
-		case 7:
+		case 6:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field PrimarySpApproval", wireType)
 			}
@@ -6439,13 +7144,13 @@ func (m *MsgCreateBucket) Unmarshal(dAtA []byte) error {
 				return io.ErrUnexpectedEOF
 			}
 			if m.PrimarySpApproval == nil {
-				m.PrimarySpApproval = &Approval{}
+				m.PrimarySpApproval = &common.Approval{}
 			}
 			if err := m.PrimarySpApproval.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
 				return err
 			}
 			iNdEx = postIndex
-		case 8:
+		case 7:
 			if wireType != 0 {
 				return fmt.Errorf("proto: wrong wireType = %d for field ChargedReadQuota", wireType)
 			}
@@ -7154,7 +7859,7 @@ func (m *MsgCreateObject) Unmarshal(dAtA []byte) error {
 				return io.ErrUnexpectedEOF
 			}
 			if m.PrimarySpApproval == nil {
-				m.PrimarySpApproval = &Approval{}
+				m.PrimarySpApproval = &common.Approval{}
 			}
 			if err := m.PrimarySpApproval.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
 				return err
@@ -7211,38 +7916,6 @@ func (m *MsgCreateObject) Unmarshal(dAtA []byte) error {
 					break
 				}
 			}
-		case 10:
-			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field ExpectSecondarySpAddresses", wireType)
-			}
-			var stringLen uint64
-			for shift := uint(0); ; shift += 7 {
-				if shift >= 64 {
-					return ErrIntOverflowTx
-				}
-				if iNdEx >= l {
-					return io.ErrUnexpectedEOF
-				}
-				b := dAtA[iNdEx]
-				iNdEx++
-				stringLen |= uint64(b&0x7F) << shift
-				if b < 0x80 {
-					break
-				}
-			}
-			intStringLen := int(stringLen)
-			if intStringLen < 0 {
-				return ErrInvalidLengthTx
-			}
-			postIndex := iNdEx + intStringLen
-			if postIndex < 0 {
-				return ErrInvalidLengthTx
-			}
-			if postIndex > l {
-				return io.ErrUnexpectedEOF
-			}
-			m.ExpectSecondarySpAddresses = append(m.ExpectSecondarySpAddresses, string(dAtA[iNdEx:postIndex]))
-			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipTx(dAtA[iNdEx:])
@@ -7474,10 +8147,10 @@ func (m *MsgSealObject) Unmarshal(dAtA []byte) error {
 			m.ObjectName = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
 		case 4:
-			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field SecondarySpAddresses", wireType)
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupId", wireType)
 			}
-			var stringLen uint64
+			m.GlobalVirtualGroupId = 0
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowTx
@@ -7487,27 +8160,14 @@ func (m *MsgSealObject) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				stringLen |= uint64(b&0x7F) << shift
+				m.GlobalVirtualGroupId |= uint32(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
-			intStringLen := int(stringLen)
-			if intStringLen < 0 {
-				return ErrInvalidLengthTx
-			}
-			postIndex := iNdEx + intStringLen
-			if postIndex < 0 {
-				return ErrInvalidLengthTx
-			}
-			if postIndex > l {
-				return io.ErrUnexpectedEOF
-			}
-			m.SecondarySpAddresses = append(m.SecondarySpAddresses, string(dAtA[iNdEx:postIndex]))
-			iNdEx = postIndex
 		case 5:
 			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field SecondarySpSignatures", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field SecondarySpBlsAggSignatures", wireType)
 			}
 			var byteLen int
 			for shift := uint(0); ; shift += 7 {
@@ -7534,8 +8194,10 @@ func (m *MsgSealObject) Unmarshal(dAtA []byte) error {
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			m.SecondarySpSignatures = append(m.SecondarySpSignatures, make([]byte, postIndex-iNdEx))
-			copy(m.SecondarySpSignatures[len(m.SecondarySpSignatures)-1], dAtA[iNdEx:postIndex])
+			m.SecondarySpBlsAggSignatures = append(m.SecondarySpBlsAggSignatures[:0], dAtA[iNdEx:postIndex]...)
+			if m.SecondarySpBlsAggSignatures == nil {
+				m.SecondarySpBlsAggSignatures = []byte{}
+			}
 			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
@@ -8023,7 +8685,7 @@ func (m *MsgCopyObject) Unmarshal(dAtA []byte) error {
 				return io.ErrUnexpectedEOF
 			}
 			if m.DstPrimarySpApproval == nil {
-				m.DstPrimarySpApproval = &Approval{}
+				m.DstPrimarySpApproval = &common.Approval{}
 			}
 			if err := m.DstPrimarySpApproval.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
 				return err
@@ -11663,6 +12325,606 @@ func (m *MsgUpdateParamsResponse) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
+func (m *MsgMigrateBucket) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgMigrateBucket: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgMigrateBucket: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Operator", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Operator = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field BucketName", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.BucketName = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field DstPrimarySpId", wireType)
+			}
+			m.DstPrimarySpId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.DstPrimarySpId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 4:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field DstPrimarySpApproval", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.DstPrimarySpApproval == nil {
+				m.DstPrimarySpApproval = &common.Approval{}
+			}
+			if err := m.DstPrimarySpApproval.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgMigrateBucketResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgMigrateBucketResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgMigrateBucketResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgCompleteMigrateBucket) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgCompleteMigrateBucket: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgCompleteMigrateBucket: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Operator", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Operator = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field BucketName", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.BucketName = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupFamilyId", wireType)
+			}
+			m.GlobalVirtualGroupFamilyId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.GlobalVirtualGroupFamilyId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 4:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GvgMappings", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.GvgMappings = append(m.GvgMappings, &GVGMapping{})
+			if err := m.GvgMappings[len(m.GvgMappings)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgCompleteMigrateBucketResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgCompleteMigrateBucketResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgCompleteMigrateBucketResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgCancelMigrateBucket) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgCancelMigrateBucket: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgCancelMigrateBucket: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Operator", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Operator = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field BucketName", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.BucketName = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgCancelMigrateBucketResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgCancelMigrateBucketResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgCancelMigrateBucketResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
 func skipTx(dAtA []byte) (n int, err error) {
 	l := len(dAtA)
 	iNdEx := 0
diff --git a/x/storage/types/types.go b/x/storage/types/types.go
index 9fd708905..d0f8d68b6 100644
--- a/x/storage/types/types.go
+++ b/x/storage/types/types.go
@@ -18,15 +18,6 @@ const (
 	MaxPaginationLimit = 200 // the default limit is 100 if pagination parameters is not provided
 )
 
-func EncodeSequence(u Uint) []byte {
-	return u.Bytes()
-}
-
-func DecodeSequence(bz []byte) Uint {
-	u := sdkmath.NewUint(0)
-	return u.SetBytes(bz)
-}
-
 func (m *BucketInfo) ToNFTMetadata() *BucketMetaData {
 	return &BucketMetaData{
 		BucketName: m.BucketName,
@@ -34,6 +25,15 @@ func (m *BucketInfo) ToNFTMetadata() *BucketMetaData {
 	}
 }
 
+func (m *BucketInfo) CheckBucketStatus() error {
+	if m.BucketStatus == BUCKET_STATUS_DISCONTINUED {
+		return ErrBucketDiscontinued
+	} else if m.BucketStatus == BUCKET_STATUS_MIGRATING {
+		return ErrBucketMigrating
+	}
+	return nil
+}
+
 func (m *ObjectInfo) ToNFTMetadata() *ObjectMetaData {
 	return &ObjectMetaData{
 		ObjectName: m.ObjectName,
@@ -83,3 +83,48 @@ func (di *DeleteInfo) IsEmpty() bool {
 	}
 	return isBucketIdsEmpty && isObjectIdsEmpty && isGroupIdsEmpty
 }
+
+func (b *InternalBucketInfo) GetLVGByGVGID(gvgID uint32) (*LocalVirtualGroup, bool) {
+	for _, lvg := range b.LocalVirtualGroups {
+		if lvg.GlobalVirtualGroupId == gvgID {
+			return lvg, true
+		}
+	}
+	return nil, false
+}
+
+func (b *InternalBucketInfo) AppendLVG(lvg *LocalVirtualGroup) {
+	if len(b.LocalVirtualGroups) != 0 {
+		lastLVG := b.LocalVirtualGroups[len(b.LocalVirtualGroups)-1]
+		if lvg.Id <= lastLVG.Id {
+			panic("Not allow to append a lvg which id is smaller than the last lvg")
+		}
+	}
+	b.LocalVirtualGroups = append(b.LocalVirtualGroups, lvg)
+}
+
+func (b *InternalBucketInfo) GetMaxLVGID() uint32 {
+	if len(b.LocalVirtualGroups) == 0 {
+		return 0
+	} else {
+		lastLVG := b.LocalVirtualGroups[len(b.LocalVirtualGroups)-1]
+		return lastLVG.Id
+	}
+}
+
+func (b *InternalBucketInfo) GetLVG(lvgID uint32) (*LocalVirtualGroup, bool) {
+	for _, lvg := range b.LocalVirtualGroups {
+		if lvg.Id == lvgID {
+			return lvg, true
+		}
+	}
+	return nil, false
+}
+
+func (b *InternalBucketInfo) MustGetLVG(lvgID uint32) *LocalVirtualGroup {
+	lvg, found := b.GetLVG(lvgID)
+	if !found {
+		panic("lvg not found in internal bucket info")
+	}
+	return lvg
+}
diff --git a/x/storage/types/types.pb.go b/x/storage/types/types.pb.go
index 3d9e15645..150248e84 100644
--- a/x/storage/types/types.pb.go
+++ b/x/storage/types/types.pb.go
@@ -7,6 +7,7 @@ import (
 	fmt "fmt"
 	_ "github.com/bnb-chain/greenfield/x/payment/types"
 	_ "github.com/cosmos/cosmos-proto"
+	_ "github.com/cosmos/cosmos-sdk/types"
 	_ "github.com/cosmos/gogoproto/gogoproto"
 	proto "github.com/cosmos/gogoproto/proto"
 	io "io"
@@ -40,15 +41,15 @@ type BucketInfo struct {
 	CreateAt int64 `protobuf:"varint,6,opt,name=create_at,json=createAt,proto3" json:"create_at,omitempty"`
 	// payment_address is the address of the payment account
 	PaymentAddress string `protobuf:"bytes,7,opt,name=payment_address,json=paymentAddress,proto3" json:"payment_address,omitempty"`
-	// primary_sp_address is the address of the primary sp. Objects belongs to this bucket will never
+	// primary_sp_id is the unique id of the primary sp. Objects belongs to this bucket will never
 	// leave this SP, unless you explicitly shift them to another SP.
-	PrimarySpAddress string `protobuf:"bytes,8,opt,name=primary_sp_address,json=primarySpAddress,proto3" json:"primary_sp_address,omitempty"`
+	PrimarySpId uint32 `protobuf:"varint,8,opt,name=primary_sp_id,json=primarySpId,proto3" json:"primary_sp_id,omitempty"`
+	// global_virtual_group_family_id defines the unique id of gvg family
+	GlobalVirtualGroupFamilyId uint32 `protobuf:"varint,9,opt,name=global_virtual_group_family_id,json=globalVirtualGroupFamilyId,proto3" json:"global_virtual_group_family_id,omitempty"`
 	// charged_read_quota defines the traffic quota for read in bytes per month.
 	// The available read data for each user is the sum of the free read data provided by SP and
 	// the ChargeReadQuota specified here.
-	ChargedReadQuota uint64 `protobuf:"varint,9,opt,name=charged_read_quota,json=chargedReadQuota,proto3" json:"charged_read_quota,omitempty"`
-	// billing info of the bucket
-	BillingInfo BillingInfo `protobuf:"bytes,10,opt,name=billing_info,json=billingInfo,proto3" json:"billing_info"`
+	ChargedReadQuota uint64 `protobuf:"varint,10,opt,name=charged_read_quota,json=chargedReadQuota,proto3" json:"charged_read_quota,omitempty"`
 	// bucket_status define the status of the bucket.
 	BucketStatus BucketStatus `protobuf:"varint,11,opt,name=bucket_status,json=bucketStatus,proto3,enum=greenfield.storage.BucketStatus" json:"bucket_status,omitempty"`
 }
@@ -128,25 +129,25 @@ func (m *BucketInfo) GetPaymentAddress() string {
 	return ""
 }
 
-func (m *BucketInfo) GetPrimarySpAddress() string {
+func (m *BucketInfo) GetPrimarySpId() uint32 {
 	if m != nil {
-		return m.PrimarySpAddress
+		return m.PrimarySpId
 	}
-	return ""
+	return 0
 }
 
-func (m *BucketInfo) GetChargedReadQuota() uint64 {
+func (m *BucketInfo) GetGlobalVirtualGroupFamilyId() uint32 {
 	if m != nil {
-		return m.ChargedReadQuota
+		return m.GlobalVirtualGroupFamilyId
 	}
 	return 0
 }
 
-func (m *BucketInfo) GetBillingInfo() BillingInfo {
+func (m *BucketInfo) GetChargedReadQuota() uint64 {
 	if m != nil {
-		return m.BillingInfo
+		return m.ChargedReadQuota
 	}
-	return BillingInfo{}
+	return 0
 }
 
 func (m *BucketInfo) GetBucketStatus() BucketStatus {
@@ -156,28 +157,29 @@ func (m *BucketInfo) GetBucketStatus() BucketStatus {
 	return BUCKET_STATUS_CREATED
 }
 
-// BillingInfo is the billing information of the bucket
-type BillingInfo struct {
+type InternalBucketInfo struct {
 	// the time of the payment price, used to calculate the charge rate of the bucket
 	PriceTime int64 `protobuf:"varint,1,opt,name=price_time,json=priceTime,proto3" json:"price_time,omitempty"`
 	// the total size of the objects in the bucket, used to calculate the charge rate of the bucket
 	TotalChargeSize uint64 `protobuf:"varint,2,opt,name=total_charge_size,json=totalChargeSize,proto3" json:"total_charge_size,omitempty"`
-	// secondary sp objects size statistics
-	SecondarySpObjectsSize []SecondarySpObjectsSize `protobuf:"bytes,3,rep,name=secondary_sp_objects_size,json=secondarySpObjectsSize,proto3" json:"secondary_sp_objects_size"`
+	// local_virtual_groups contains all the lvg of this bucket.
+	LocalVirtualGroups []*LocalVirtualGroup `protobuf:"bytes,3,rep,name=local_virtual_groups,json=localVirtualGroups,proto3" json:"local_virtual_groups,omitempty"`
+	// next_local_virtual_group_id store the next id used by local virtual group
+	NextLocalVirtualGroupId uint32 `protobuf:"varint,4,opt,name=next_local_virtual_group_id,json=nextLocalVirtualGroupId,proto3" json:"next_local_virtual_group_id,omitempty"`
 }
 
-func (m *BillingInfo) Reset()         { *m = BillingInfo{} }
-func (m *BillingInfo) String() string { return proto.CompactTextString(m) }
-func (*BillingInfo) ProtoMessage()    {}
-func (*BillingInfo) Descriptor() ([]byte, []int) {
+func (m *InternalBucketInfo) Reset()         { *m = InternalBucketInfo{} }
+func (m *InternalBucketInfo) String() string { return proto.CompactTextString(m) }
+func (*InternalBucketInfo) ProtoMessage()    {}
+func (*InternalBucketInfo) Descriptor() ([]byte, []int) {
 	return fileDescriptor_bf95fa2efdc74d97, []int{1}
 }
-func (m *BillingInfo) XXX_Unmarshal(b []byte) error {
+func (m *InternalBucketInfo) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
 }
-func (m *BillingInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+func (m *InternalBucketInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
 	if deterministic {
-		return xxx_messageInfo_BillingInfo.Marshal(b, m, deterministic)
+		return xxx_messageInfo_InternalBucketInfo.Marshal(b, m, deterministic)
 	} else {
 		b = b[:cap(b)]
 		n, err := m.MarshalToSizedBuffer(b)
@@ -187,128 +189,82 @@ func (m *BillingInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error)
 		return b[:n], nil
 	}
 }
-func (m *BillingInfo) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_BillingInfo.Merge(m, src)
+func (m *InternalBucketInfo) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_InternalBucketInfo.Merge(m, src)
 }
-func (m *BillingInfo) XXX_Size() int {
+func (m *InternalBucketInfo) XXX_Size() int {
 	return m.Size()
 }
-func (m *BillingInfo) XXX_DiscardUnknown() {
-	xxx_messageInfo_BillingInfo.DiscardUnknown(m)
+func (m *InternalBucketInfo) XXX_DiscardUnknown() {
+	xxx_messageInfo_InternalBucketInfo.DiscardUnknown(m)
 }
 
-var xxx_messageInfo_BillingInfo proto.InternalMessageInfo
+var xxx_messageInfo_InternalBucketInfo proto.InternalMessageInfo
 
-func (m *BillingInfo) GetPriceTime() int64 {
+func (m *InternalBucketInfo) GetPriceTime() int64 {
 	if m != nil {
 		return m.PriceTime
 	}
 	return 0
 }
 
-func (m *BillingInfo) GetTotalChargeSize() uint64 {
+func (m *InternalBucketInfo) GetTotalChargeSize() uint64 {
 	if m != nil {
 		return m.TotalChargeSize
 	}
 	return 0
 }
 
-func (m *BillingInfo) GetSecondarySpObjectsSize() []SecondarySpObjectsSize {
+func (m *InternalBucketInfo) GetLocalVirtualGroups() []*LocalVirtualGroup {
 	if m != nil {
-		return m.SecondarySpObjectsSize
+		return m.LocalVirtualGroups
 	}
 	return nil
 }
 
-// secondary sp objects size statistics
-type SecondarySpObjectsSize struct {
-	// address is the address of the secondary sp
-	SpAddress string `protobuf:"bytes,1,opt,name=sp_address,json=spAddress,proto3" json:"sp_address,omitempty"`
-	// size is the total size of the objects in the secondary sp
-	TotalChargeSize uint64 `protobuf:"varint,2,opt,name=total_charge_size,json=totalChargeSize,proto3" json:"total_charge_size,omitempty"`
-}
-
-func (m *SecondarySpObjectsSize) Reset()         { *m = SecondarySpObjectsSize{} }
-func (m *SecondarySpObjectsSize) String() string { return proto.CompactTextString(m) }
-func (*SecondarySpObjectsSize) ProtoMessage()    {}
-func (*SecondarySpObjectsSize) Descriptor() ([]byte, []int) {
-	return fileDescriptor_bf95fa2efdc74d97, []int{2}
-}
-func (m *SecondarySpObjectsSize) XXX_Unmarshal(b []byte) error {
-	return m.Unmarshal(b)
-}
-func (m *SecondarySpObjectsSize) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	if deterministic {
-		return xxx_messageInfo_SecondarySpObjectsSize.Marshal(b, m, deterministic)
-	} else {
-		b = b[:cap(b)]
-		n, err := m.MarshalToSizedBuffer(b)
-		if err != nil {
-			return nil, err
-		}
-		return b[:n], nil
-	}
-}
-func (m *SecondarySpObjectsSize) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_SecondarySpObjectsSize.Merge(m, src)
-}
-func (m *SecondarySpObjectsSize) XXX_Size() int {
-	return m.Size()
-}
-func (m *SecondarySpObjectsSize) XXX_DiscardUnknown() {
-	xxx_messageInfo_SecondarySpObjectsSize.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_SecondarySpObjectsSize proto.InternalMessageInfo
-
-func (m *SecondarySpObjectsSize) GetSpAddress() string {
-	if m != nil {
-		return m.SpAddress
-	}
-	return ""
-}
-
-func (m *SecondarySpObjectsSize) GetTotalChargeSize() uint64 {
+func (m *InternalBucketInfo) GetNextLocalVirtualGroupId() uint32 {
 	if m != nil {
-		return m.TotalChargeSize
+		return m.NextLocalVirtualGroupId
 	}
 	return 0
 }
 
 type ObjectInfo struct {
+	// owner is the object owner
 	Owner string `protobuf:"bytes,1,opt,name=owner,proto3" json:"owner,omitempty"`
+	// creator is the address of the uploader, it always be same as owner address
+	Creator string `protobuf:"bytes,2,opt,name=creator,proto3" json:"creator,omitempty"`
 	// bucket_name is the name of the bucket
-	BucketName string `protobuf:"bytes,2,opt,name=bucket_name,json=bucketName,proto3" json:"bucket_name,omitempty"`
+	BucketName string `protobuf:"bytes,3,opt,name=bucket_name,json=bucketName,proto3" json:"bucket_name,omitempty"`
 	// object_name is the name of object
-	ObjectName string `protobuf:"bytes,3,opt,name=object_name,json=objectName,proto3" json:"object_name,omitempty"`
+	ObjectName string `protobuf:"bytes,4,opt,name=object_name,json=objectName,proto3" json:"object_name,omitempty"`
 	// id is the unique identifier of object
-	Id Uint `protobuf:"bytes,4,opt,name=id,proto3,customtype=Uint" json:"id"`
+	Id                  Uint   `protobuf:"bytes,5,opt,name=id,proto3,customtype=Uint" json:"id"`
+	LocalVirtualGroupId uint32 `protobuf:"varint,6,opt,name=local_virtual_group_id,json=localVirtualGroupId,proto3" json:"local_virtual_group_id,omitempty"`
 	// payloadSize is the total size of the object payload
-	PayloadSize uint64 `protobuf:"varint,5,opt,name=payload_size,json=payloadSize,proto3" json:"payload_size,omitempty"`
+	PayloadSize uint64 `protobuf:"varint,7,opt,name=payload_size,json=payloadSize,proto3" json:"payload_size,omitempty"`
 	// visibility defines the highest permissions for object. When an object is public, everyone can access it.
-	Visibility VisibilityType `protobuf:"varint,6,opt,name=visibility,proto3,enum=greenfield.storage.VisibilityType" json:"visibility,omitempty"`
+	Visibility VisibilityType `protobuf:"varint,8,opt,name=visibility,proto3,enum=greenfield.storage.VisibilityType" json:"visibility,omitempty"`
 	// content_type define the format of the object which should be a standard MIME type.
-	ContentType string `protobuf:"bytes,7,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"`
+	ContentType string `protobuf:"bytes,9,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"`
 	// create_at define the block timestamp when the object is created
-	CreateAt int64 `protobuf:"varint,8,opt,name=create_at,json=createAt,proto3" json:"create_at,omitempty"`
+	CreateAt int64 `protobuf:"varint,10,opt,name=create_at,json=createAt,proto3" json:"create_at,omitempty"`
 	// object_status define the upload status of the object.
-	ObjectStatus ObjectStatus `protobuf:"varint,9,opt,name=object_status,json=objectStatus,proto3,enum=greenfield.storage.ObjectStatus" json:"object_status,omitempty"`
+	ObjectStatus ObjectStatus `protobuf:"varint,11,opt,name=object_status,json=objectStatus,proto3,enum=greenfield.storage.ObjectStatus" json:"object_status,omitempty"`
 	// redundancy_type define the type of the redundancy which can be multi-replication or EC.
-	RedundancyType RedundancyType `protobuf:"varint,10,opt,name=redundancy_type,json=redundancyType,proto3,enum=greenfield.storage.RedundancyType" json:"redundancy_type,omitempty"`
+	RedundancyType RedundancyType `protobuf:"varint,12,opt,name=redundancy_type,json=redundancyType,proto3,enum=greenfield.storage.RedundancyType" json:"redundancy_type,omitempty"`
 	// source_type define the source of the object.
-	SourceType SourceType `protobuf:"varint,11,opt,name=source_type,json=sourceType,proto3,enum=greenfield.storage.SourceType" json:"source_type,omitempty"`
+	SourceType SourceType `protobuf:"varint,13,opt,name=source_type,json=sourceType,proto3,enum=greenfield.storage.SourceType" json:"source_type,omitempty"`
 	// checksums define the root hash of the pieces which stored in a SP.
 	// add omit tag to omit the field when converting to NFT metadata
-	Checksums [][]byte `protobuf:"bytes,12,rep,name=checksums,proto3" json:"checksums,omitempty" traits:"omit"`
-	// secondary_sp_addresses define the addresses of secondary_sps
-	SecondarySpAddresses []string `protobuf:"bytes,13,rep,name=secondary_sp_addresses,json=secondarySpAddresses,proto3" json:"secondary_sp_addresses,omitempty"`
+	Checksums [][]byte `protobuf:"bytes,14,rep,name=checksums,proto3" json:"checksums,omitempty" traits:"omit"`
 }
 
 func (m *ObjectInfo) Reset()         { *m = ObjectInfo{} }
 func (m *ObjectInfo) String() string { return proto.CompactTextString(m) }
 func (*ObjectInfo) ProtoMessage()    {}
 func (*ObjectInfo) Descriptor() ([]byte, []int) {
-	return fileDescriptor_bf95fa2efdc74d97, []int{3}
+	return fileDescriptor_bf95fa2efdc74d97, []int{2}
 }
 func (m *ObjectInfo) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -344,6 +300,13 @@ func (m *ObjectInfo) GetOwner() string {
 	return ""
 }
 
+func (m *ObjectInfo) GetCreator() string {
+	if m != nil {
+		return m.Creator
+	}
+	return ""
+}
+
 func (m *ObjectInfo) GetBucketName() string {
 	if m != nil {
 		return m.BucketName
@@ -358,6 +321,13 @@ func (m *ObjectInfo) GetObjectName() string {
 	return ""
 }
 
+func (m *ObjectInfo) GetLocalVirtualGroupId() uint32 {
+	if m != nil {
+		return m.LocalVirtualGroupId
+	}
+	return 0
+}
+
 func (m *ObjectInfo) GetPayloadSize() uint64 {
 	if m != nil {
 		return m.PayloadSize
@@ -414,13 +384,6 @@ func (m *ObjectInfo) GetChecksums() [][]byte {
 	return nil
 }
 
-func (m *ObjectInfo) GetSecondarySpAddresses() []string {
-	if m != nil {
-		return m.SecondarySpAddresses
-	}
-	return nil
-}
-
 type GroupInfo struct {
 	// owner is the owner of the group. It can not changed once it created.
 	Owner string `protobuf:"bytes,1,opt,name=owner,proto3" json:"owner,omitempty"`
@@ -438,7 +401,7 @@ func (m *GroupInfo) Reset()         { *m = GroupInfo{} }
 func (m *GroupInfo) String() string { return proto.CompactTextString(m) }
 func (*GroupInfo) ProtoMessage()    {}
 func (*GroupInfo) Descriptor() ([]byte, []int) {
-	return fileDescriptor_bf95fa2efdc74d97, []int{4}
+	return fileDescriptor_bf95fa2efdc74d97, []int{3}
 }
 func (m *GroupInfo) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -504,7 +467,7 @@ func (m *Trait) Reset()         { *m = Trait{} }
 func (m *Trait) String() string { return proto.CompactTextString(m) }
 func (*Trait) ProtoMessage()    {}
 func (*Trait) Descriptor() ([]byte, []int) {
-	return fileDescriptor_bf95fa2efdc74d97, []int{5}
+	return fileDescriptor_bf95fa2efdc74d97, []int{4}
 }
 func (m *Trait) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -564,7 +527,7 @@ func (m *BucketMetaData) Reset()         { *m = BucketMetaData{} }
 func (m *BucketMetaData) String() string { return proto.CompactTextString(m) }
 func (*BucketMetaData) ProtoMessage()    {}
 func (*BucketMetaData) Descriptor() ([]byte, []int) {
-	return fileDescriptor_bf95fa2efdc74d97, []int{6}
+	return fileDescriptor_bf95fa2efdc74d97, []int{5}
 }
 func (m *BucketMetaData) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -645,7 +608,7 @@ func (m *ObjectMetaData) Reset()         { *m = ObjectMetaData{} }
 func (m *ObjectMetaData) String() string { return proto.CompactTextString(m) }
 func (*ObjectMetaData) ProtoMessage()    {}
 func (*ObjectMetaData) Descriptor() ([]byte, []int) {
-	return fileDescriptor_bf95fa2efdc74d97, []int{7}
+	return fileDescriptor_bf95fa2efdc74d97, []int{6}
 }
 func (m *ObjectMetaData) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -726,7 +689,7 @@ func (m *GroupMetaData) Reset()         { *m = GroupMetaData{} }
 func (m *GroupMetaData) String() string { return proto.CompactTextString(m) }
 func (*GroupMetaData) ProtoMessage()    {}
 func (*GroupMetaData) Descriptor() ([]byte, []int) {
-	return fileDescriptor_bf95fa2efdc74d97, []int{8}
+	return fileDescriptor_bf95fa2efdc74d97, []int{7}
 }
 func (m *GroupMetaData) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -799,7 +762,7 @@ func (m *Ids) Reset()         { *m = Ids{} }
 func (m *Ids) String() string { return proto.CompactTextString(m) }
 func (*Ids) ProtoMessage()    {}
 func (*Ids) Descriptor() ([]byte, []int) {
-	return fileDescriptor_bf95fa2efdc74d97, []int{9}
+	return fileDescriptor_bf95fa2efdc74d97, []int{8}
 }
 func (m *Ids) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -838,7 +801,7 @@ func (m *DeleteInfo) Reset()         { *m = DeleteInfo{} }
 func (m *DeleteInfo) String() string { return proto.CompactTextString(m) }
 func (*DeleteInfo) ProtoMessage()    {}
 func (*DeleteInfo) Descriptor() ([]byte, []int) {
-	return fileDescriptor_bf95fa2efdc74d97, []int{10}
+	return fileDescriptor_bf95fa2efdc74d97, []int{9}
 }
 func (m *DeleteInfo) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -888,10 +851,71 @@ func (m *DeleteInfo) GetGroupIds() *Ids {
 	return nil
 }
 
+type MigrationBucketInfo struct {
+	SrcSpId                       uint32 `protobuf:"varint,1,opt,name=src_sp_id,json=srcSpId,proto3" json:"src_sp_id,omitempty"`
+	SrcGlobalVirtualGroupFamilyId uint32 `protobuf:"varint,2,opt,name=src_global_virtual_group_family_id,json=srcGlobalVirtualGroupFamilyId,proto3" json:"src_global_virtual_group_family_id,omitempty"`
+	DstSpId                       uint32 `protobuf:"varint,3,opt,name=dst_sp_id,json=dstSpId,proto3" json:"dst_sp_id,omitempty"`
+	// id is the unique identifier of bucket
+	BucketId Uint `protobuf:"bytes,4,opt,name=bucket_id,json=bucketId,proto3,customtype=Uint" json:"bucket_id"`
+}
+
+func (m *MigrationBucketInfo) Reset()         { *m = MigrationBucketInfo{} }
+func (m *MigrationBucketInfo) String() string { return proto.CompactTextString(m) }
+func (*MigrationBucketInfo) ProtoMessage()    {}
+func (*MigrationBucketInfo) Descriptor() ([]byte, []int) {
+	return fileDescriptor_bf95fa2efdc74d97, []int{10}
+}
+func (m *MigrationBucketInfo) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MigrationBucketInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MigrationBucketInfo.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MigrationBucketInfo) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MigrationBucketInfo.Merge(m, src)
+}
+func (m *MigrationBucketInfo) XXX_Size() int {
+	return m.Size()
+}
+func (m *MigrationBucketInfo) XXX_DiscardUnknown() {
+	xxx_messageInfo_MigrationBucketInfo.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MigrationBucketInfo proto.InternalMessageInfo
+
+func (m *MigrationBucketInfo) GetSrcSpId() uint32 {
+	if m != nil {
+		return m.SrcSpId
+	}
+	return 0
+}
+
+func (m *MigrationBucketInfo) GetSrcGlobalVirtualGroupFamilyId() uint32 {
+	if m != nil {
+		return m.SrcGlobalVirtualGroupFamilyId
+	}
+	return 0
+}
+
+func (m *MigrationBucketInfo) GetDstSpId() uint32 {
+	if m != nil {
+		return m.DstSpId
+	}
+	return 0
+}
+
 func init() {
 	proto.RegisterType((*BucketInfo)(nil), "greenfield.storage.BucketInfo")
-	proto.RegisterType((*BillingInfo)(nil), "greenfield.storage.BillingInfo")
-	proto.RegisterType((*SecondarySpObjectsSize)(nil), "greenfield.storage.SecondarySpObjectsSize")
+	proto.RegisterType((*InternalBucketInfo)(nil), "greenfield.storage.InternalBucketInfo")
 	proto.RegisterType((*ObjectInfo)(nil), "greenfield.storage.ObjectInfo")
 	proto.RegisterType((*GroupInfo)(nil), "greenfield.storage.GroupInfo")
 	proto.RegisterType((*Trait)(nil), "greenfield.storage.Trait")
@@ -900,79 +924,87 @@ func init() {
 	proto.RegisterType((*GroupMetaData)(nil), "greenfield.storage.GroupMetaData")
 	proto.RegisterType((*Ids)(nil), "greenfield.storage.Ids")
 	proto.RegisterType((*DeleteInfo)(nil), "greenfield.storage.DeleteInfo")
+	proto.RegisterType((*MigrationBucketInfo)(nil), "greenfield.storage.MigrationBucketInfo")
 }
 
 func init() { proto.RegisterFile("greenfield/storage/types.proto", fileDescriptor_bf95fa2efdc74d97) }
 
 var fileDescriptor_bf95fa2efdc74d97 = []byte{
-	// 1060 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xd1, 0x6a, 0x1b, 0x47,
-	0x17, 0xf6, 0x7a, 0x25, 0xff, 0xd6, 0x59, 0xd9, 0x4e, 0x06, 0xe1, 0x7f, 0x9d, 0x60, 0x49, 0xd9,
-	0x2b, 0x91, 0xd6, 0x12, 0xb8, 0x25, 0x85, 0x52, 0x08, 0x56, 0x93, 0xb6, 0xa6, 0x34, 0xa5, 0x2b,
-	0xa7, 0x17, 0xbd, 0x59, 0x66, 0x77, 0xc7, 0xf2, 0xd4, 0xbb, 0x3b, 0xdb, 0x99, 0xd9, 0xd4, 0x0a,
-	0xf4, 0x1d, 0xfa, 0x18, 0xbd, 0x2e, 0x79, 0x85, 0x42, 0x28, 0x14, 0x42, 0xae, 0x4a, 0x2f, 0x4c,
-	0xb1, 0xdf, 0xa0, 0x17, 0xbd, 0x2e, 0x3b, 0x33, 0xb2, 0xd7, 0xb6, 0x62, 0xe3, 0x10, 0xdf, 0x69,
-	0xce, 0x7c, 0xdf, 0xcc, 0x39, 0xdf, 0x7c, 0xe7, 0x68, 0xa1, 0x3d, 0xe6, 0x84, 0x64, 0xbb, 0x94,
-	0x24, 0xf1, 0x40, 0x48, 0xc6, 0xf1, 0x98, 0x0c, 0xe4, 0x24, 0x27, 0xa2, 0x9f, 0x73, 0x26, 0x19,
-	0x42, 0xa7, 0xfb, 0x7d, 0xb3, 0x7f, 0x67, 0x2d, 0x62, 0x22, 0x65, 0x22, 0x50, 0x88, 0x81, 0x5e,
-	0x68, 0xf8, 0x9d, 0xd6, 0x98, 0x8d, 0x99, 0x8e, 0x97, 0xbf, 0x4c, 0x74, 0xbd, 0x72, 0x49, 0x8e,
-	0x27, 0x29, 0xc9, 0xe4, 0x20, 0xc4, 0x82, 0x98, 0xed, 0xce, 0x8c, 0x1c, 0x22, 0x96, 0xa6, 0x2c,
-	0xd3, 0x00, 0xef, 0xdf, 0x1a, 0xc0, 0xb0, 0x88, 0xf6, 0x89, 0xdc, 0xce, 0x76, 0x19, 0xea, 0x43,
-	0x9d, 0xfd, 0x98, 0x11, 0xee, 0x5a, 0x5d, 0xab, 0xd7, 0x18, 0xba, 0xaf, 0x5f, 0x6c, 0xb4, 0x4c,
-	0x16, 0x5b, 0x71, 0xcc, 0x89, 0x10, 0x23, 0xc9, 0x69, 0x36, 0xf6, 0x35, 0x0c, 0x75, 0xc0, 0x09,
-	0x15, 0x3b, 0xc8, 0x70, 0x4a, 0xdc, 0xf9, 0x92, 0xe5, 0x83, 0x0e, 0x3d, 0xc1, 0x29, 0x41, 0x43,
-	0x80, 0x67, 0x54, 0xd0, 0x90, 0x26, 0x54, 0x4e, 0x5c, 0xbb, 0x6b, 0xf5, 0x96, 0x37, 0xbd, 0xfe,
-	0xc5, 0xca, 0xfb, 0xdf, 0x9e, 0xa0, 0x76, 0x26, 0x39, 0xf1, 0x2b, 0x2c, 0xf4, 0x1e, 0xcc, 0xd3,
-	0xd8, 0xad, 0xa9, 0x8c, 0xee, 0xbe, 0x3c, 0xec, 0xcc, 0xfd, 0x75, 0xd8, 0xa9, 0x3d, 0xa5, 0x99,
-	0x7c, 0xfd, 0x62, 0xc3, 0x31, 0xd9, 0x95, 0x4b, 0x7f, 0x9e, 0xc6, 0xe8, 0x21, 0x38, 0x82, 0x15,
-	0x3c, 0x22, 0x41, 0xa9, 0xb5, 0x5b, 0x57, 0x37, 0xb6, 0x67, 0xdd, 0x38, 0x52, 0x30, 0x7d, 0x9b,
-	0x38, 0xf9, 0x8d, 0xee, 0x42, 0x23, 0xe2, 0x04, 0x4b, 0x12, 0x60, 0xe9, 0x2e, 0x74, 0xad, 0x9e,
-	0xed, 0x2f, 0xea, 0xc0, 0x96, 0x44, 0x5b, 0xb0, 0x62, 0x54, 0x0e, 0xb0, 0xd6, 0xc3, 0xfd, 0xdf,
-	0x15, 0x4a, 0x2d, 0x1b, 0x82, 0x89, 0xa2, 0xcf, 0x00, 0xe5, 0x9c, 0xa6, 0x98, 0x4f, 0x02, 0x91,
-	0x9f, 0x9c, 0xb2, 0x78, 0xc5, 0x29, 0xb7, 0x0c, 0x67, 0x94, 0x4f, 0xcf, 0x79, 0x1f, 0x50, 0xb4,
-	0x87, 0xf9, 0x98, 0xc4, 0x01, 0x27, 0x38, 0x0e, 0x7e, 0x28, 0x98, 0xc4, 0x6e, 0xa3, 0x6b, 0xf5,
-	0x6a, 0xfe, 0x2d, 0xb3, 0xe3, 0x13, 0x1c, 0x7f, 0x53, 0xc6, 0xd1, 0x17, 0xd0, 0x0c, 0x69, 0x92,
-	0xd0, 0x6c, 0x1c, 0xd0, 0x6c, 0x97, 0xb9, 0xd0, 0xb5, 0x7a, 0xce, 0x66, 0x67, 0x96, 0x2e, 0x43,
-	0x8d, 0x2b, 0xfd, 0x30, 0xac, 0x95, 0x72, 0xfb, 0x4e, 0x78, 0x1a, 0x42, 0x8f, 0x61, 0xc9, 0x3c,
-	0xb9, 0x90, 0x58, 0x16, 0xc2, 0x75, 0x94, 0xc4, 0xdd, 0x99, 0x47, 0x29, 0xe0, 0x48, 0xe1, 0xfc,
-	0x66, 0x58, 0x59, 0x79, 0xbf, 0x59, 0xe0, 0x54, 0x6e, 0x42, 0xeb, 0x00, 0x39, 0xa7, 0xe5, 0xb3,
-	0xd1, 0x94, 0x28, 0xfb, 0xd9, 0x7e, 0x43, 0x45, 0x76, 0x68, 0x4a, 0xd0, 0x7d, 0xb8, 0x2d, 0x99,
-	0xc4, 0x49, 0xa0, 0x2b, 0x0b, 0x04, 0x7d, 0xae, 0xed, 0x56, 0xf3, 0x57, 0xd4, 0xc6, 0xa7, 0x2a,
-	0x3e, 0xa2, 0xcf, 0x09, 0xda, 0x87, 0x35, 0x41, 0x22, 0x96, 0xc5, 0x46, 0x63, 0x16, 0x7e, 0x4f,
-	0x22, 0x29, 0x34, 0xc7, 0xee, 0xda, 0x3d, 0x67, 0xf3, 0xfe, 0x4c, 0x43, 0x4c, 0x49, 0xa3, 0xfc,
-	0x6b, 0x4d, 0x29, 0x8f, 0x33, 0x1a, 0xac, 0x8a, 0x99, 0xbb, 0xde, 0x4f, 0xb0, 0x3a, 0x9b, 0x87,
-	0x3e, 0x02, 0xa8, 0x3c, 0xf0, 0x55, 0x0d, 0xd5, 0x10, 0x27, 0x2f, 0x7b, 0x8d, 0x5a, 0xbd, 0x5f,
-	0xea, 0x00, 0xfa, 0xd2, 0x9b, 0xe9, 0xdf, 0x0e, 0x38, 0x5a, 0x3e, 0x0d, 0xb0, 0x35, 0x40, 0x87,
-	0x14, 0xe0, 0x5a, 0xcd, 0x79, 0x0f, 0x9a, 0x39, 0x9e, 0x24, 0x0c, 0xc7, 0xba, 0xa8, 0xba, 0x2a,
-	0xca, 0x31, 0x31, 0xa5, 0xda, 0xd9, 0x81, 0xb1, 0xf0, 0x56, 0x03, 0xe3, 0x1e, 0x34, 0x23, 0x96,
-	0xc9, 0xb2, 0x4b, 0xd5, 0x10, 0x50, 0x2d, 0xea, 0x3b, 0x26, 0x76, 0xb1, 0xcb, 0x17, 0xcf, 0x75,
-	0xf9, 0x63, 0x58, 0x32, 0x45, 0x1b, 0x8b, 0x37, 0xde, 0x6c, 0x71, 0x2d, 0xfe, 0xd4, 0xe2, 0xac,
-	0xb2, 0x42, 0x5f, 0xc2, 0x0a, 0x27, 0x71, 0x91, 0xc5, 0x38, 0x8b, 0x26, 0x3a, 0x13, 0x78, 0x73,
-	0x3d, 0xfe, 0x09, 0x54, 0xd5, 0xb3, 0xcc, 0xcf, 0xac, 0xcf, 0xcf, 0x35, 0xe7, 0xda, 0x73, 0x6d,
-	0x00, 0x8d, 0x68, 0x8f, 0x44, 0xfb, 0xa2, 0x48, 0x85, 0xdb, 0xec, 0xda, 0xbd, 0xe6, 0xf0, 0xf6,
-	0x3f, 0x87, 0x9d, 0x25, 0xc9, 0x31, 0x95, 0xe2, 0x63, 0x8f, 0xa5, 0x54, 0x7a, 0xfe, 0x29, 0x06,
-	0x3d, 0x81, 0xd5, 0x33, 0x6d, 0x64, 0x9c, 0x4c, 0x84, 0xbb, 0xd4, 0xb5, 0x2f, 0x35, 0x57, 0xab,
-	0xd2, 0x2b, 0x5b, 0x53, 0x96, 0x77, 0x68, 0x41, 0xe3, 0x73, 0xce, 0x8a, 0xfc, 0xad, 0x9c, 0xba,
-	0x0e, 0x30, 0x2e, 0xc9, 0x55, 0xa3, 0x36, 0x54, 0x44, 0xd9, 0xf0, 0x9c, 0x3c, 0xf6, 0xb5, 0xe5,
-	0xb9, 0x96, 0x8f, 0x5b, 0x50, 0x27, 0x07, 0x92, 0x63, 0x65, 0xe0, 0x86, 0xaf, 0x17, 0xde, 0x27,
-	0x50, 0xdf, 0x29, 0xc5, 0x2c, 0x73, 0x55, 0xaa, 0xea, 0x5c, 0x2c, 0x9d, 0xab, 0x8a, 0xa8, 0xab,
-	0x5a, 0x50, 0x7f, 0x86, 0x93, 0x62, 0x5a, 0x85, 0x5e, 0x78, 0x7f, 0x58, 0xb0, 0xac, 0xe7, 0xe5,
-	0x57, 0x44, 0xe2, 0x47, 0x58, 0x62, 0xd4, 0x05, 0x27, 0x26, 0x22, 0xe2, 0x34, 0x97, 0x94, 0x65,
-	0xe6, 0xa0, 0x6a, 0xa8, 0x74, 0x3a, 0x39, 0x90, 0x84, 0x67, 0x38, 0x09, 0x0a, 0x9e, 0x98, 0x13,
-	0x9d, 0x69, 0xec, 0x29, 0x4f, 0xce, 0xb7, 0xb8, 0x7d, 0xa1, 0xc5, 0x5b, 0x50, 0xa7, 0x29, 0x1e,
-	0x13, 0x5d, 0xbc, 0xaf, 0x17, 0xe8, 0x21, 0x00, 0x96, 0x92, 0xd3, 0xb0, 0x90, 0x44, 0xb8, 0x75,
-	0x35, 0x35, 0xd7, 0x66, 0xe9, 0xa9, 0x4a, 0x36, 0x43, 0xb2, 0x42, 0x51, 0xf5, 0xe8, 0xe6, 0x78,
-	0xe7, 0xf5, 0x5c, 0x3e, 0x91, 0x6e, 0xa8, 0x9e, 0xdf, 0x2d, 0x58, 0x52, 0xf6, 0x7d, 0xb7, 0xe5,
-	0x9c, 0xf5, 0xb5, 0x7d, 0xde, 0xd7, 0x37, 0x54, 0xcc, 0x26, 0xd8, 0xdb, 0xb1, 0x30, 0xa6, 0xb7,
-	0x54, 0x3b, 0x5f, 0x65, 0x7a, 0xef, 0x57, 0x0b, 0xe0, 0x11, 0x49, 0x88, 0x24, 0xaa, 0x81, 0x1f,
-	0x80, 0x31, 0x51, 0x40, 0x63, 0xfd, 0xf7, 0xe6, 0x6c, 0xfe, 0x7f, 0x56, 0x0e, 0xdb, 0xb1, 0xf0,
-	0x1b, 0x1a, 0x5a, 0xde, 0xf9, 0x00, 0xcc, 0x63, 0x29, 0xde, 0xfc, 0x15, 0x3c, 0x0d, 0x2d, 0x79,
-	0x1f, 0x82, 0x96, 0x45, 0xd1, 0xec, 0xcb, 0x69, 0x8b, 0x0a, 0xb9, 0x1d, 0x8b, 0xe1, 0xf6, 0xcb,
-	0xa3, 0xb6, 0xf5, 0xea, 0xa8, 0x6d, 0xfd, 0x7d, 0xd4, 0xb6, 0x7e, 0x3e, 0x6e, 0xcf, 0xbd, 0x3a,
-	0x6e, 0xcf, 0xfd, 0x79, 0xdc, 0x9e, 0xfb, 0x6e, 0x30, 0xa6, 0x72, 0xaf, 0x08, 0xfb, 0x11, 0x4b,
-	0x07, 0x61, 0x16, 0x6e, 0x44, 0x7b, 0x98, 0x66, 0x83, 0xca, 0xf7, 0xf2, 0xc1, 0xd9, 0xaf, 0xf6,
-	0x70, 0x41, 0x7d, 0x31, 0x7f, 0xf0, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb9, 0xd9, 0x4b, 0xae,
-	0xd8, 0x0b, 0x00, 0x00,
+	// 1180 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0xdf, 0x6e, 0xdb, 0xb6,
+	0x17, 0x8e, 0x22, 0xbb, 0x89, 0x8e, 0xe2, 0xf4, 0x57, 0xd5, 0xf8, 0x55, 0x4d, 0x51, 0xdb, 0x15,
+	0xb0, 0xc1, 0xd8, 0x56, 0x1b, 0x4d, 0x87, 0x62, 0x18, 0x0a, 0x14, 0xf5, 0xda, 0x15, 0xc6, 0xd6,
+	0x0d, 0x53, 0xda, 0x0e, 0xd8, 0x8d, 0x40, 0x49, 0x8c, 0xc2, 0x55, 0x12, 0x3d, 0x92, 0x4a, 0xe3,
+	0x3e, 0xc5, 0x9e, 0x65, 0xe8, 0x0b, 0xec, 0xae, 0x18, 0x36, 0xa0, 0xe8, 0xd5, 0xb0, 0x8b, 0x60,
+	0x48, 0xde, 0x60, 0x17, 0xbb, 0x1e, 0x44, 0xd2, 0xa9, 0x62, 0x3b, 0xcd, 0x1f, 0xb4, 0x77, 0xe6,
+	0xe1, 0x77, 0xc4, 0x73, 0xbe, 0xf3, 0x9d, 0x43, 0x1a, 0x5a, 0x09, 0xc3, 0x38, 0xdf, 0x24, 0x38,
+	0x8d, 0xfb, 0x5c, 0x50, 0x86, 0x12, 0xdc, 0x17, 0xe3, 0x11, 0xe6, 0xbd, 0x11, 0xa3, 0x82, 0x3a,
+	0xce, 0x9b, 0xfd, 0x9e, 0xde, 0x5f, 0x6b, 0x45, 0x94, 0x67, 0x94, 0xf7, 0x43, 0xc4, 0x71, 0x7f,
+	0xfb, 0x46, 0x88, 0x05, 0xba, 0xd1, 0x8f, 0x28, 0xc9, 0x95, 0xcf, 0xda, 0x65, 0xb5, 0x1f, 0xc8,
+	0x55, 0x5f, 0x2d, 0xf4, 0x56, 0x33, 0xa1, 0x09, 0x55, 0xf6, 0xf2, 0x97, 0xb6, 0x5e, 0xab, 0x04,
+	0x31, 0x42, 0xe3, 0x0c, 0xe7, 0xa2, 0x4f, 0x0b, 0x11, 0x6c, 0xa6, 0xf4, 0x99, 0x86, 0x7c, 0x38,
+	0x07, 0xc2, 0x05, 0xc3, 0x28, 0x0b, 0x18, 0x8e, 0x28, 0x8b, 0x35, 0xae, 0x3d, 0x27, 0x9f, 0x88,
+	0x66, 0x19, 0xd5, 0xc1, 0x79, 0xbf, 0xd7, 0x00, 0x06, 0x45, 0xf4, 0x14, 0x8b, 0x61, 0xbe, 0x49,
+	0x9d, 0x1e, 0xd4, 0xe9, 0xb3, 0x1c, 0x33, 0xd7, 0xe8, 0x18, 0x5d, 0x6b, 0xe0, 0xbe, 0x7e, 0x71,
+	0xbd, 0xa9, 0x23, 0xbe, 0x1b, 0xc7, 0x0c, 0x73, 0xbe, 0x21, 0x18, 0xc9, 0x13, 0x5f, 0xc1, 0x9c,
+	0x36, 0xd8, 0xa1, 0xf4, 0x0e, 0x72, 0x94, 0x61, 0x77, 0xb1, 0xf4, 0xf2, 0x41, 0x99, 0xbe, 0x41,
+	0x19, 0x76, 0x06, 0x00, 0xdb, 0x84, 0x93, 0x90, 0xa4, 0x44, 0x8c, 0x5d, 0xb3, 0x63, 0x74, 0x57,
+	0xd7, 0xbd, 0xde, 0x2c, 0x8b, 0xbd, 0x27, 0x07, 0xa8, 0x47, 0xe3, 0x11, 0xf6, 0x2b, 0x5e, 0xce,
+	0xc7, 0xb0, 0x48, 0x62, 0xb7, 0x26, 0x23, 0xba, 0xf2, 0x72, 0xb7, 0xbd, 0xf0, 0xd7, 0x6e, 0xbb,
+	0xf6, 0x98, 0xe4, 0xe2, 0xf5, 0x8b, 0xeb, 0xb6, 0x8e, 0xae, 0x5c, 0xfa, 0x8b, 0x24, 0x76, 0xee,
+	0x80, 0xcd, 0x69, 0xc1, 0x22, 0x1c, 0x94, 0x75, 0x73, 0xeb, 0xf2, 0xc4, 0xd6, 0xbc, 0x13, 0x37,
+	0x24, 0x4c, 0x9d, 0xc6, 0x0f, 0x7e, 0x3b, 0x57, 0xc0, 0x8a, 0x18, 0x46, 0x02, 0x07, 0x48, 0xb8,
+	0xe7, 0x3a, 0x46, 0xd7, 0xf4, 0x97, 0x95, 0xe1, 0xae, 0x70, 0xee, 0xc2, 0x79, 0x4d, 0x77, 0x80,
+	0x14, 0x1f, 0xee, 0xd2, 0x31, 0x4c, 0xad, 0x6a, 0x07, 0x6d, 0x75, 0x3c, 0x68, 0x8c, 0x18, 0xc9,
+	0x10, 0x1b, 0x07, 0x7c, 0x14, 0x90, 0xd8, 0x5d, 0xee, 0x18, 0xdd, 0x86, 0x6f, 0x6b, 0xe3, 0xc6,
+	0x68, 0x18, 0x3b, 0x03, 0x68, 0x25, 0x29, 0x0d, 0x51, 0x1a, 0x6c, 0x13, 0x26, 0x0a, 0x94, 0x06,
+	0x09, 0xa3, 0xc5, 0x28, 0xd8, 0x44, 0x19, 0x49, 0xc7, 0xa5, 0x93, 0x25, 0x9d, 0xd6, 0x14, 0xea,
+	0x89, 0x02, 0x3d, 0x28, 0x31, 0x5f, 0x4a, 0xc8, 0x30, 0x76, 0x3e, 0x01, 0x27, 0xda, 0x42, 0x2c,
+	0xc1, 0x71, 0xc0, 0x30, 0x8a, 0x83, 0x9f, 0x0a, 0x2a, 0x90, 0x0b, 0x1d, 0xa3, 0x5b, 0xf3, 0xff,
+	0xa7, 0x77, 0x7c, 0x8c, 0xe2, 0xef, 0x4a, 0xbb, 0x73, 0x1f, 0x1a, 0xba, 0x90, 0x5c, 0x20, 0x51,
+	0x70, 0xd7, 0x96, 0xc4, 0x75, 0xe6, 0x11, 0xa7, 0xf4, 0xb2, 0x21, 0x71, 0xfe, 0x4a, 0x58, 0x59,
+	0x79, 0xff, 0x1a, 0xe0, 0x0c, 0x73, 0x81, 0x59, 0x8e, 0xd2, 0x8a, 0xac, 0xae, 0x02, 0x8c, 0x18,
+	0x29, 0x6b, 0x42, 0x32, 0x2c, 0xb5, 0x65, 0xfa, 0x96, 0xb4, 0x3c, 0x22, 0x19, 0x76, 0x3e, 0x82,
+	0x0b, 0x82, 0x0a, 0x94, 0x06, 0x2a, 0xac, 0x80, 0x93, 0xe7, 0x4a, 0x4b, 0x35, 0xff, 0xbc, 0xdc,
+	0xf8, 0x42, 0xda, 0x37, 0xc8, 0x73, 0xec, 0x7c, 0x0f, 0xcd, 0x94, 0x46, 0xd3, 0xcc, 0x70, 0xd7,
+	0xec, 0x98, 0x5d, 0x7b, 0xfd, 0x83, 0x79, 0xf1, 0x7e, 0x5d, 0xe2, 0xab, 0x1c, 0xf9, 0x4e, 0x3a,
+	0x6d, 0xe2, 0xce, 0x6d, 0xb8, 0x92, 0xe3, 0x1d, 0x11, 0xcc, 0xf9, 0x7a, 0xa0, 0xe5, 0xd7, 0xf0,
+	0x2f, 0x95, 0x90, 0x99, 0xef, 0x0d, 0x63, 0xef, 0xd7, 0x3a, 0xc0, 0xb7, 0xe1, 0x8f, 0x38, 0x3a,
+	0x5b, 0x1f, 0xad, 0xc3, 0x92, 0xd4, 0x18, 0x65, 0xaa, 0x87, 0xde, 0xe2, 0x31, 0x01, 0x4e, 0xf7,
+	0x9e, 0x39, 0xd3, 0x7b, 0x6d, 0xb0, 0xa9, 0x0c, 0x49, 0x01, 0x6a, 0x0a, 0xa0, 0x4c, 0x12, 0xa0,
+	0x1a, 0xab, 0x7e, 0xb2, 0xc6, 0xba, 0x09, 0xff, 0x3f, 0x82, 0x9a, 0x73, 0x92, 0x9a, 0x8b, 0xe9,
+	0x2c, 0x2d, 0xce, 0x35, 0x58, 0x19, 0xa1, 0x71, 0x4a, 0x51, 0xac, 0x8a, 0xba, 0x24, 0x8b, 0x6a,
+	0x6b, 0x9b, 0x2c, 0xe8, 0xe1, 0x09, 0xb1, 0x7c, 0xa6, 0x09, 0x71, 0x0d, 0x56, 0x22, 0x9a, 0x8b,
+	0xb2, 0x2d, 0x65, 0xd7, 0x5b, 0x32, 0x55, 0x5b, 0xdb, 0x66, 0xdb, 0x1a, 0xa6, 0xda, 0xfa, 0x3e,
+	0x34, 0x34, 0x53, 0xc7, 0xab, 0x5f, 0x55, 0x79, 0xa2, 0x7e, 0x5a, 0x59, 0x39, 0x5f, 0xc1, 0x79,
+	0x86, 0xe3, 0x22, 0x8f, 0x51, 0x1e, 0x8d, 0x55, 0x24, 0x2b, 0x47, 0xe7, 0xe3, 0x1f, 0x40, 0x65,
+	0x3e, 0xab, 0xec, 0xd0, 0x7a, 0x7a, 0x90, 0x35, 0x4e, 0x3d, 0xc8, 0xfa, 0x60, 0x45, 0x5b, 0x38,
+	0x7a, 0xca, 0x8b, 0x8c, 0xbb, 0xab, 0x1d, 0xb3, 0xbb, 0x32, 0xb8, 0xf0, 0xcf, 0x6e, 0xbb, 0x21,
+	0x18, 0x22, 0x82, 0x7f, 0xee, 0xd1, 0x8c, 0x08, 0xcf, 0x7f, 0x83, 0xf1, 0x76, 0x0d, 0xb0, 0x54,
+	0xe1, 0xce, 0x22, 0xe1, 0xab, 0x00, 0x4a, 0x11, 0x95, 0x9b, 0xc0, 0x92, 0x16, 0xa9, 0xb5, 0xa9,
+	0x74, 0xcc, 0x53, 0xa7, 0x73, 0xaa, 0x5b, 0xa0, 0x09, 0x75, 0xbc, 0x23, 0x18, 0x52, 0xe2, 0xf6,
+	0xd5, 0xc2, 0xbb, 0x0d, 0xf5, 0x47, 0x65, 0xf2, 0x65, 0xac, 0x92, 0x05, 0x15, 0x8b, 0xa1, 0x62,
+	0x95, 0x16, 0x79, 0x54, 0x13, 0xea, 0xdb, 0x28, 0x2d, 0x26, 0x59, 0xa8, 0x85, 0xf7, 0x87, 0x01,
+	0xab, 0x6a, 0xa6, 0x3d, 0xc4, 0x02, 0xdd, 0x43, 0x02, 0x39, 0x1d, 0xb0, 0x63, 0xcc, 0x23, 0x46,
+	0x46, 0x82, 0xd0, 0x5c, 0x7f, 0xa8, 0x6a, 0x2a, 0x95, 0x89, 0x77, 0xd4, 0x3c, 0x0c, 0x0a, 0x96,
+	0xea, 0x2f, 0xda, 0x13, 0xdb, 0x63, 0x96, 0x1e, 0xdf, 0xc7, 0x4d, 0xa8, 0x93, 0x0c, 0x25, 0x93,
+	0x0e, 0x56, 0x0b, 0xe7, 0x0e, 0x00, 0x12, 0x82, 0x91, 0xb0, 0x10, 0x98, 0xbb, 0x75, 0x39, 0xfe,
+	0x2e, 0xcf, 0xe3, 0x53, 0xa6, 0x3c, 0xa8, 0x95, 0x94, 0xf9, 0x15, 0x17, 0x99, 0x8f, 0x12, 0xf3,
+	0x3b, 0xcf, 0xa7, 0x3a, 0x76, 0xcc, 0x99, 0xb1, 0xf3, 0x9e, 0xf2, 0xf9, 0xcd, 0x80, 0x86, 0x94,
+	0xef, 0xbb, 0x4d, 0xe7, 0xb0, 0xae, 0xcd, 0x69, 0x5d, 0xbf, 0xa7, 0x64, 0xd6, 0xc1, 0x1c, 0xc6,
+	0x5c, 0x8b, 0xde, 0xe8, 0x98, 0x27, 0x10, 0xbd, 0xf7, 0x8b, 0x01, 0x70, 0x0f, 0xa7, 0x58, 0x60,
+	0xd9, 0xc0, 0xb7, 0x40, 0x8b, 0x28, 0x20, 0x31, 0x97, 0xc9, 0xdb, 0xeb, 0x97, 0xe6, 0xc5, 0x30,
+	0x8c, 0xb9, 0x6f, 0x29, 0x68, 0x79, 0xe6, 0x2d, 0xd0, 0xc5, 0x92, 0x7e, 0x8b, 0xc7, 0xf8, 0x29,
+	0x68, 0xe9, 0xf7, 0x29, 0x58, 0x93, 0x2b, 0x81, 0x4b, 0x9e, 0xde, 0xe2, 0xb6, 0x9c, 0xa8, 0x0b,
+	0x82, 0x7b, 0xaf, 0x0d, 0xb8, 0xf8, 0x90, 0x24, 0x0c, 0x95, 0xf5, 0xa8, 0x3c, 0x19, 0xd6, 0xc0,
+	0xe2, 0x2c, 0xd2, 0x4f, 0x24, 0x43, 0xde, 0x30, 0x4b, 0x9c, 0x45, 0xf2, 0x79, 0x34, 0x04, 0xaf,
+	0xdc, 0x3b, 0xe6, 0x89, 0xb4, 0x28, 0x9d, 0xae, 0x72, 0x16, 0x3d, 0x38, 0xfa, 0x95, 0xb4, 0x06,
+	0x56, 0xcc, 0x85, 0x3e, 0xc6, 0x54, 0xc7, 0xc4, 0x5c, 0xc8, 0x63, 0x3e, 0x03, 0xeb, 0x80, 0xc0,
+	0x93, 0x0c, 0x9e, 0xe5, 0x09, 0x87, 0x83, 0xe1, 0xcb, 0xbd, 0x96, 0xf1, 0x6a, 0xaf, 0x65, 0xfc,
+	0xbd, 0xd7, 0x32, 0x7e, 0xde, 0x6f, 0x2d, 0xbc, 0xda, 0x6f, 0x2d, 0xfc, 0xb9, 0xdf, 0x5a, 0xf8,
+	0xa1, 0x9f, 0x10, 0xb1, 0x55, 0x84, 0xbd, 0x88, 0x66, 0xfd, 0x30, 0x0f, 0xaf, 0x47, 0x5b, 0x88,
+	0xe4, 0xfd, 0xca, 0x2b, 0x7d, 0xe7, 0xf0, 0xff, 0x8e, 0xf0, 0x9c, 0x7c, 0xa7, 0xdf, 0xfc, 0x2f,
+	0x00, 0x00, 0xff, 0xff, 0xe9, 0x7b, 0x5b, 0x1a, 0x9a, 0x0c, 0x00, 0x00,
 }
 
 func (m *BucketInfo) Marshal() (dAtA []byte, err error) {
@@ -1000,27 +1032,20 @@ func (m *BucketInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		i--
 		dAtA[i] = 0x58
 	}
-	{
-		size, err := m.BillingInfo.MarshalToSizedBuffer(dAtA[:i])
-		if err != nil {
-			return 0, err
-		}
-		i -= size
-		i = encodeVarintTypes(dAtA, i, uint64(size))
-	}
-	i--
-	dAtA[i] = 0x52
 	if m.ChargedReadQuota != 0 {
 		i = encodeVarintTypes(dAtA, i, uint64(m.ChargedReadQuota))
 		i--
+		dAtA[i] = 0x50
+	}
+	if m.GlobalVirtualGroupFamilyId != 0 {
+		i = encodeVarintTypes(dAtA, i, uint64(m.GlobalVirtualGroupFamilyId))
+		i--
 		dAtA[i] = 0x48
 	}
-	if len(m.PrimarySpAddress) > 0 {
-		i -= len(m.PrimarySpAddress)
-		copy(dAtA[i:], m.PrimarySpAddress)
-		i = encodeVarintTypes(dAtA, i, uint64(len(m.PrimarySpAddress)))
+	if m.PrimarySpId != 0 {
+		i = encodeVarintTypes(dAtA, i, uint64(m.PrimarySpId))
 		i--
-		dAtA[i] = 0x42
+		dAtA[i] = 0x40
 	}
 	if len(m.PaymentAddress) > 0 {
 		i -= len(m.PaymentAddress)
@@ -1071,7 +1096,7 @@ func (m *BucketInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	return len(dAtA) - i, nil
 }
 
-func (m *BillingInfo) Marshal() (dAtA []byte, err error) {
+func (m *InternalBucketInfo) Marshal() (dAtA []byte, err error) {
 	size := m.Size()
 	dAtA = make([]byte, size)
 	n, err := m.MarshalToSizedBuffer(dAtA[:size])
@@ -1081,20 +1106,25 @@ func (m *BillingInfo) Marshal() (dAtA []byte, err error) {
 	return dAtA[:n], nil
 }
 
-func (m *BillingInfo) MarshalTo(dAtA []byte) (int, error) {
+func (m *InternalBucketInfo) MarshalTo(dAtA []byte) (int, error) {
 	size := m.Size()
 	return m.MarshalToSizedBuffer(dAtA[:size])
 }
 
-func (m *BillingInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+func (m *InternalBucketInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	i := len(dAtA)
 	_ = i
 	var l int
 	_ = l
-	if len(m.SecondarySpObjectsSize) > 0 {
-		for iNdEx := len(m.SecondarySpObjectsSize) - 1; iNdEx >= 0; iNdEx-- {
+	if m.NextLocalVirtualGroupId != 0 {
+		i = encodeVarintTypes(dAtA, i, uint64(m.NextLocalVirtualGroupId))
+		i--
+		dAtA[i] = 0x20
+	}
+	if len(m.LocalVirtualGroups) > 0 {
+		for iNdEx := len(m.LocalVirtualGroups) - 1; iNdEx >= 0; iNdEx-- {
 			{
-				size, err := m.SecondarySpObjectsSize[iNdEx].MarshalToSizedBuffer(dAtA[:i])
+				size, err := m.LocalVirtualGroups[iNdEx].MarshalToSizedBuffer(dAtA[:i])
 				if err != nil {
 					return 0, err
 				}
@@ -1118,41 +1148,6 @@ func (m *BillingInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	return len(dAtA) - i, nil
 }
 
-func (m *SecondarySpObjectsSize) Marshal() (dAtA []byte, err error) {
-	size := m.Size()
-	dAtA = make([]byte, size)
-	n, err := m.MarshalToSizedBuffer(dAtA[:size])
-	if err != nil {
-		return nil, err
-	}
-	return dAtA[:n], nil
-}
-
-func (m *SecondarySpObjectsSize) MarshalTo(dAtA []byte) (int, error) {
-	size := m.Size()
-	return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *SecondarySpObjectsSize) MarshalToSizedBuffer(dAtA []byte) (int, error) {
-	i := len(dAtA)
-	_ = i
-	var l int
-	_ = l
-	if m.TotalChargeSize != 0 {
-		i = encodeVarintTypes(dAtA, i, uint64(m.TotalChargeSize))
-		i--
-		dAtA[i] = 0x10
-	}
-	if len(m.SpAddress) > 0 {
-		i -= len(m.SpAddress)
-		copy(dAtA[i:], m.SpAddress)
-		i = encodeVarintTypes(dAtA, i, uint64(len(m.SpAddress)))
-		i--
-		dAtA[i] = 0xa
-	}
-	return len(dAtA) - i, nil
-}
-
 func (m *ObjectInfo) Marshal() (dAtA []byte, err error) {
 	size := m.Size()
 	dAtA = make([]byte, size)
@@ -1173,60 +1168,56 @@ func (m *ObjectInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	_ = i
 	var l int
 	_ = l
-	if len(m.SecondarySpAddresses) > 0 {
-		for iNdEx := len(m.SecondarySpAddresses) - 1; iNdEx >= 0; iNdEx-- {
-			i -= len(m.SecondarySpAddresses[iNdEx])
-			copy(dAtA[i:], m.SecondarySpAddresses[iNdEx])
-			i = encodeVarintTypes(dAtA, i, uint64(len(m.SecondarySpAddresses[iNdEx])))
-			i--
-			dAtA[i] = 0x6a
-		}
-	}
 	if len(m.Checksums) > 0 {
 		for iNdEx := len(m.Checksums) - 1; iNdEx >= 0; iNdEx-- {
 			i -= len(m.Checksums[iNdEx])
 			copy(dAtA[i:], m.Checksums[iNdEx])
 			i = encodeVarintTypes(dAtA, i, uint64(len(m.Checksums[iNdEx])))
 			i--
-			dAtA[i] = 0x62
+			dAtA[i] = 0x72
 		}
 	}
 	if m.SourceType != 0 {
 		i = encodeVarintTypes(dAtA, i, uint64(m.SourceType))
 		i--
-		dAtA[i] = 0x58
+		dAtA[i] = 0x68
 	}
 	if m.RedundancyType != 0 {
 		i = encodeVarintTypes(dAtA, i, uint64(m.RedundancyType))
 		i--
-		dAtA[i] = 0x50
+		dAtA[i] = 0x60
 	}
 	if m.ObjectStatus != 0 {
 		i = encodeVarintTypes(dAtA, i, uint64(m.ObjectStatus))
 		i--
-		dAtA[i] = 0x48
+		dAtA[i] = 0x58
 	}
 	if m.CreateAt != 0 {
 		i = encodeVarintTypes(dAtA, i, uint64(m.CreateAt))
 		i--
-		dAtA[i] = 0x40
+		dAtA[i] = 0x50
 	}
 	if len(m.ContentType) > 0 {
 		i -= len(m.ContentType)
 		copy(dAtA[i:], m.ContentType)
 		i = encodeVarintTypes(dAtA, i, uint64(len(m.ContentType)))
 		i--
-		dAtA[i] = 0x3a
+		dAtA[i] = 0x4a
 	}
 	if m.Visibility != 0 {
 		i = encodeVarintTypes(dAtA, i, uint64(m.Visibility))
 		i--
-		dAtA[i] = 0x30
+		dAtA[i] = 0x40
 	}
 	if m.PayloadSize != 0 {
 		i = encodeVarintTypes(dAtA, i, uint64(m.PayloadSize))
 		i--
-		dAtA[i] = 0x28
+		dAtA[i] = 0x38
+	}
+	if m.LocalVirtualGroupId != 0 {
+		i = encodeVarintTypes(dAtA, i, uint64(m.LocalVirtualGroupId))
+		i--
+		dAtA[i] = 0x30
 	}
 	{
 		size := m.Id.Size()
@@ -1237,19 +1228,26 @@ func (m *ObjectInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		i = encodeVarintTypes(dAtA, i, uint64(size))
 	}
 	i--
-	dAtA[i] = 0x22
+	dAtA[i] = 0x2a
 	if len(m.ObjectName) > 0 {
 		i -= len(m.ObjectName)
 		copy(dAtA[i:], m.ObjectName)
 		i = encodeVarintTypes(dAtA, i, uint64(len(m.ObjectName)))
 		i--
-		dAtA[i] = 0x1a
+		dAtA[i] = 0x22
 	}
 	if len(m.BucketName) > 0 {
 		i -= len(m.BucketName)
 		copy(dAtA[i:], m.BucketName)
 		i = encodeVarintTypes(dAtA, i, uint64(len(m.BucketName)))
 		i--
+		dAtA[i] = 0x1a
+	}
+	if len(m.Creator) > 0 {
+		i -= len(m.Creator)
+		copy(dAtA[i:], m.Creator)
+		i = encodeVarintTypes(dAtA, i, uint64(len(m.Creator)))
+		i--
 		dAtA[i] = 0x12
 	}
 	if len(m.Owner) > 0 {
@@ -1649,6 +1647,54 @@ func (m *DeleteInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	return len(dAtA) - i, nil
 }
 
+func (m *MigrationBucketInfo) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MigrationBucketInfo) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MigrationBucketInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	{
+		size := m.BucketId.Size()
+		i -= size
+		if _, err := m.BucketId.MarshalTo(dAtA[i:]); err != nil {
+			return 0, err
+		}
+		i = encodeVarintTypes(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0x22
+	if m.DstSpId != 0 {
+		i = encodeVarintTypes(dAtA, i, uint64(m.DstSpId))
+		i--
+		dAtA[i] = 0x18
+	}
+	if m.SrcGlobalVirtualGroupFamilyId != 0 {
+		i = encodeVarintTypes(dAtA, i, uint64(m.SrcGlobalVirtualGroupFamilyId))
+		i--
+		dAtA[i] = 0x10
+	}
+	if m.SrcSpId != 0 {
+		i = encodeVarintTypes(dAtA, i, uint64(m.SrcSpId))
+		i--
+		dAtA[i] = 0x8
+	}
+	return len(dAtA) - i, nil
+}
+
 func encodeVarintTypes(dAtA []byte, offset int, v uint64) int {
 	offset -= sovTypes(v)
 	base := offset
@@ -1689,22 +1735,22 @@ func (m *BucketInfo) Size() (n int) {
 	if l > 0 {
 		n += 1 + l + sovTypes(uint64(l))
 	}
-	l = len(m.PrimarySpAddress)
-	if l > 0 {
-		n += 1 + l + sovTypes(uint64(l))
+	if m.PrimarySpId != 0 {
+		n += 1 + sovTypes(uint64(m.PrimarySpId))
+	}
+	if m.GlobalVirtualGroupFamilyId != 0 {
+		n += 1 + sovTypes(uint64(m.GlobalVirtualGroupFamilyId))
 	}
 	if m.ChargedReadQuota != 0 {
 		n += 1 + sovTypes(uint64(m.ChargedReadQuota))
 	}
-	l = m.BillingInfo.Size()
-	n += 1 + l + sovTypes(uint64(l))
 	if m.BucketStatus != 0 {
 		n += 1 + sovTypes(uint64(m.BucketStatus))
 	}
 	return n
 }
 
-func (m *BillingInfo) Size() (n int) {
+func (m *InternalBucketInfo) Size() (n int) {
 	if m == nil {
 		return 0
 	}
@@ -1716,27 +1762,14 @@ func (m *BillingInfo) Size() (n int) {
 	if m.TotalChargeSize != 0 {
 		n += 1 + sovTypes(uint64(m.TotalChargeSize))
 	}
-	if len(m.SecondarySpObjectsSize) > 0 {
-		for _, e := range m.SecondarySpObjectsSize {
+	if len(m.LocalVirtualGroups) > 0 {
+		for _, e := range m.LocalVirtualGroups {
 			l = e.Size()
 			n += 1 + l + sovTypes(uint64(l))
 		}
 	}
-	return n
-}
-
-func (m *SecondarySpObjectsSize) Size() (n int) {
-	if m == nil {
-		return 0
-	}
-	var l int
-	_ = l
-	l = len(m.SpAddress)
-	if l > 0 {
-		n += 1 + l + sovTypes(uint64(l))
-	}
-	if m.TotalChargeSize != 0 {
-		n += 1 + sovTypes(uint64(m.TotalChargeSize))
+	if m.NextLocalVirtualGroupId != 0 {
+		n += 1 + sovTypes(uint64(m.NextLocalVirtualGroupId))
 	}
 	return n
 }
@@ -1751,6 +1784,10 @@ func (m *ObjectInfo) Size() (n int) {
 	if l > 0 {
 		n += 1 + l + sovTypes(uint64(l))
 	}
+	l = len(m.Creator)
+	if l > 0 {
+		n += 1 + l + sovTypes(uint64(l))
+	}
 	l = len(m.BucketName)
 	if l > 0 {
 		n += 1 + l + sovTypes(uint64(l))
@@ -1761,6 +1798,9 @@ func (m *ObjectInfo) Size() (n int) {
 	}
 	l = m.Id.Size()
 	n += 1 + l + sovTypes(uint64(l))
+	if m.LocalVirtualGroupId != 0 {
+		n += 1 + sovTypes(uint64(m.LocalVirtualGroupId))
+	}
 	if m.PayloadSize != 0 {
 		n += 1 + sovTypes(uint64(m.PayloadSize))
 	}
@@ -1789,12 +1829,6 @@ func (m *ObjectInfo) Size() (n int) {
 			n += 1 + l + sovTypes(uint64(l))
 		}
 	}
-	if len(m.SecondarySpAddresses) > 0 {
-		for _, s := range m.SecondarySpAddresses {
-			l = len(s)
-			n += 1 + l + sovTypes(uint64(l))
-		}
-	}
 	return n
 }
 
@@ -1970,6 +2004,26 @@ func (m *DeleteInfo) Size() (n int) {
 	return n
 }
 
+func (m *MigrationBucketInfo) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.SrcSpId != 0 {
+		n += 1 + sovTypes(uint64(m.SrcSpId))
+	}
+	if m.SrcGlobalVirtualGroupFamilyId != 0 {
+		n += 1 + sovTypes(uint64(m.SrcGlobalVirtualGroupFamilyId))
+	}
+	if m.DstSpId != 0 {
+		n += 1 + sovTypes(uint64(m.DstSpId))
+	}
+	l = m.BucketId.Size()
+	n += 1 + l + sovTypes(uint64(l))
+	return n
+}
+
 func sovTypes(x uint64) (n int) {
 	return (math_bits.Len64(x|1) + 6) / 7
 }
@@ -2193,10 +2247,10 @@ func (m *BucketInfo) Unmarshal(dAtA []byte) error {
 			m.PaymentAddress = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
 		case 8:
-			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field PrimarySpAddress", wireType)
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field PrimarySpId", wireType)
 			}
-			var stringLen uint64
+			m.PrimarySpId = 0
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowTypes
@@ -2206,29 +2260,16 @@ func (m *BucketInfo) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				stringLen |= uint64(b&0x7F) << shift
+				m.PrimarySpId |= uint32(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
-			intStringLen := int(stringLen)
-			if intStringLen < 0 {
-				return ErrInvalidLengthTypes
+		case 9:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupFamilyId", wireType)
 			}
-			postIndex := iNdEx + intStringLen
-			if postIndex < 0 {
-				return ErrInvalidLengthTypes
-			}
-			if postIndex > l {
-				return io.ErrUnexpectedEOF
-			}
-			m.PrimarySpAddress = string(dAtA[iNdEx:postIndex])
-			iNdEx = postIndex
-		case 9:
-			if wireType != 0 {
-				return fmt.Errorf("proto: wrong wireType = %d for field ChargedReadQuota", wireType)
-			}
-			m.ChargedReadQuota = 0
+			m.GlobalVirtualGroupFamilyId = 0
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowTypes
@@ -2238,16 +2279,16 @@ func (m *BucketInfo) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				m.ChargedReadQuota |= uint64(b&0x7F) << shift
+				m.GlobalVirtualGroupFamilyId |= uint32(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
 		case 10:
-			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field BillingInfo", wireType)
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ChargedReadQuota", wireType)
 			}
-			var msglen int
+			m.ChargedReadQuota = 0
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowTypes
@@ -2257,25 +2298,11 @@ func (m *BucketInfo) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				msglen |= int(b&0x7F) << shift
+				m.ChargedReadQuota |= uint64(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
-			if msglen < 0 {
-				return ErrInvalidLengthTypes
-			}
-			postIndex := iNdEx + msglen
-			if postIndex < 0 {
-				return ErrInvalidLengthTypes
-			}
-			if postIndex > l {
-				return io.ErrUnexpectedEOF
-			}
-			if err := m.BillingInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
-				return err
-			}
-			iNdEx = postIndex
 		case 11:
 			if wireType != 0 {
 				return fmt.Errorf("proto: wrong wireType = %d for field BucketStatus", wireType)
@@ -2316,7 +2343,7 @@ func (m *BucketInfo) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
-func (m *BillingInfo) Unmarshal(dAtA []byte) error {
+func (m *InternalBucketInfo) Unmarshal(dAtA []byte) error {
 	l := len(dAtA)
 	iNdEx := 0
 	for iNdEx < l {
@@ -2339,10 +2366,10 @@ func (m *BillingInfo) Unmarshal(dAtA []byte) error {
 		fieldNum := int32(wire >> 3)
 		wireType := int(wire & 0x7)
 		if wireType == 4 {
-			return fmt.Errorf("proto: BillingInfo: wiretype end group for non-group")
+			return fmt.Errorf("proto: InternalBucketInfo: wiretype end group for non-group")
 		}
 		if fieldNum <= 0 {
-			return fmt.Errorf("proto: BillingInfo: illegal tag %d (wire type %d)", fieldNum, wire)
+			return fmt.Errorf("proto: InternalBucketInfo: illegal tag %d (wire type %d)", fieldNum, wire)
 		}
 		switch fieldNum {
 		case 1:
@@ -2385,7 +2412,7 @@ func (m *BillingInfo) Unmarshal(dAtA []byte) error {
 			}
 		case 3:
 			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field SecondarySpObjectsSize", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field LocalVirtualGroups", wireType)
 			}
 			var msglen int
 			for shift := uint(0); ; shift += 7 {
@@ -2412,11 +2439,30 @@ func (m *BillingInfo) Unmarshal(dAtA []byte) error {
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			m.SecondarySpObjectsSize = append(m.SecondarySpObjectsSize, SecondarySpObjectsSize{})
-			if err := m.SecondarySpObjectsSize[len(m.SecondarySpObjectsSize)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+			m.LocalVirtualGroups = append(m.LocalVirtualGroups, &LocalVirtualGroup{})
+			if err := m.LocalVirtualGroups[len(m.LocalVirtualGroups)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
 				return err
 			}
 			iNdEx = postIndex
+		case 4:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field NextLocalVirtualGroupId", wireType)
+			}
+			m.NextLocalVirtualGroupId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTypes
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.NextLocalVirtualGroupId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
 		default:
 			iNdEx = preIndex
 			skippy, err := skipTypes(dAtA[iNdEx:])
@@ -2438,7 +2484,7 @@ func (m *BillingInfo) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
-func (m *SecondarySpObjectsSize) Unmarshal(dAtA []byte) error {
+func (m *ObjectInfo) Unmarshal(dAtA []byte) error {
 	l := len(dAtA)
 	iNdEx := 0
 	for iNdEx < l {
@@ -2461,15 +2507,15 @@ func (m *SecondarySpObjectsSize) Unmarshal(dAtA []byte) error {
 		fieldNum := int32(wire >> 3)
 		wireType := int(wire & 0x7)
 		if wireType == 4 {
-			return fmt.Errorf("proto: SecondarySpObjectsSize: wiretype end group for non-group")
+			return fmt.Errorf("proto: ObjectInfo: wiretype end group for non-group")
 		}
 		if fieldNum <= 0 {
-			return fmt.Errorf("proto: SecondarySpObjectsSize: illegal tag %d (wire type %d)", fieldNum, wire)
+			return fmt.Errorf("proto: ObjectInfo: illegal tag %d (wire type %d)", fieldNum, wire)
 		}
 		switch fieldNum {
 		case 1:
 			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field SpAddress", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field Owner", wireType)
 			}
 			var stringLen uint64
 			for shift := uint(0); ; shift += 7 {
@@ -2497,80 +2543,11 @@ func (m *SecondarySpObjectsSize) Unmarshal(dAtA []byte) error {
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			m.SpAddress = string(dAtA[iNdEx:postIndex])
+			m.Owner = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
 		case 2:
-			if wireType != 0 {
-				return fmt.Errorf("proto: wrong wireType = %d for field TotalChargeSize", wireType)
-			}
-			m.TotalChargeSize = 0
-			for shift := uint(0); ; shift += 7 {
-				if shift >= 64 {
-					return ErrIntOverflowTypes
-				}
-				if iNdEx >= l {
-					return io.ErrUnexpectedEOF
-				}
-				b := dAtA[iNdEx]
-				iNdEx++
-				m.TotalChargeSize |= uint64(b&0x7F) << shift
-				if b < 0x80 {
-					break
-				}
-			}
-		default:
-			iNdEx = preIndex
-			skippy, err := skipTypes(dAtA[iNdEx:])
-			if err != nil {
-				return err
-			}
-			if (skippy < 0) || (iNdEx+skippy) < 0 {
-				return ErrInvalidLengthTypes
-			}
-			if (iNdEx + skippy) > l {
-				return io.ErrUnexpectedEOF
-			}
-			iNdEx += skippy
-		}
-	}
-
-	if iNdEx > l {
-		return io.ErrUnexpectedEOF
-	}
-	return nil
-}
-func (m *ObjectInfo) Unmarshal(dAtA []byte) error {
-	l := len(dAtA)
-	iNdEx := 0
-	for iNdEx < l {
-		preIndex := iNdEx
-		var wire uint64
-		for shift := uint(0); ; shift += 7 {
-			if shift >= 64 {
-				return ErrIntOverflowTypes
-			}
-			if iNdEx >= l {
-				return io.ErrUnexpectedEOF
-			}
-			b := dAtA[iNdEx]
-			iNdEx++
-			wire |= uint64(b&0x7F) << shift
-			if b < 0x80 {
-				break
-			}
-		}
-		fieldNum := int32(wire >> 3)
-		wireType := int(wire & 0x7)
-		if wireType == 4 {
-			return fmt.Errorf("proto: ObjectInfo: wiretype end group for non-group")
-		}
-		if fieldNum <= 0 {
-			return fmt.Errorf("proto: ObjectInfo: illegal tag %d (wire type %d)", fieldNum, wire)
-		}
-		switch fieldNum {
-		case 1:
 			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field Owner", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field Creator", wireType)
 			}
 			var stringLen uint64
 			for shift := uint(0); ; shift += 7 {
@@ -2598,9 +2575,9 @@ func (m *ObjectInfo) Unmarshal(dAtA []byte) error {
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			m.Owner = string(dAtA[iNdEx:postIndex])
+			m.Creator = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
-		case 2:
+		case 3:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field BucketName", wireType)
 			}
@@ -2632,7 +2609,7 @@ func (m *ObjectInfo) Unmarshal(dAtA []byte) error {
 			}
 			m.BucketName = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
-		case 3:
+		case 4:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field ObjectName", wireType)
 			}
@@ -2664,7 +2641,7 @@ func (m *ObjectInfo) Unmarshal(dAtA []byte) error {
 			}
 			m.ObjectName = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
-		case 4:
+		case 5:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType)
 			}
@@ -2698,7 +2675,26 @@ func (m *ObjectInfo) Unmarshal(dAtA []byte) error {
 				return err
 			}
 			iNdEx = postIndex
-		case 5:
+		case 6:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field LocalVirtualGroupId", wireType)
+			}
+			m.LocalVirtualGroupId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTypes
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.LocalVirtualGroupId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 7:
 			if wireType != 0 {
 				return fmt.Errorf("proto: wrong wireType = %d for field PayloadSize", wireType)
 			}
@@ -2717,7 +2713,7 @@ func (m *ObjectInfo) Unmarshal(dAtA []byte) error {
 					break
 				}
 			}
-		case 6:
+		case 8:
 			if wireType != 0 {
 				return fmt.Errorf("proto: wrong wireType = %d for field Visibility", wireType)
 			}
@@ -2736,7 +2732,7 @@ func (m *ObjectInfo) Unmarshal(dAtA []byte) error {
 					break
 				}
 			}
-		case 7:
+		case 9:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field ContentType", wireType)
 			}
@@ -2768,7 +2764,7 @@ func (m *ObjectInfo) Unmarshal(dAtA []byte) error {
 			}
 			m.ContentType = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
-		case 8:
+		case 10:
 			if wireType != 0 {
 				return fmt.Errorf("proto: wrong wireType = %d for field CreateAt", wireType)
 			}
@@ -2787,7 +2783,7 @@ func (m *ObjectInfo) Unmarshal(dAtA []byte) error {
 					break
 				}
 			}
-		case 9:
+		case 11:
 			if wireType != 0 {
 				return fmt.Errorf("proto: wrong wireType = %d for field ObjectStatus", wireType)
 			}
@@ -2806,7 +2802,7 @@ func (m *ObjectInfo) Unmarshal(dAtA []byte) error {
 					break
 				}
 			}
-		case 10:
+		case 12:
 			if wireType != 0 {
 				return fmt.Errorf("proto: wrong wireType = %d for field RedundancyType", wireType)
 			}
@@ -2825,7 +2821,7 @@ func (m *ObjectInfo) Unmarshal(dAtA []byte) error {
 					break
 				}
 			}
-		case 11:
+		case 13:
 			if wireType != 0 {
 				return fmt.Errorf("proto: wrong wireType = %d for field SourceType", wireType)
 			}
@@ -2844,7 +2840,7 @@ func (m *ObjectInfo) Unmarshal(dAtA []byte) error {
 					break
 				}
 			}
-		case 12:
+		case 14:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field Checksums", wireType)
 			}
@@ -2876,38 +2872,6 @@ func (m *ObjectInfo) Unmarshal(dAtA []byte) error {
 			m.Checksums = append(m.Checksums, make([]byte, postIndex-iNdEx))
 			copy(m.Checksums[len(m.Checksums)-1], dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
-		case 13:
-			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field SecondarySpAddresses", wireType)
-			}
-			var stringLen uint64
-			for shift := uint(0); ; shift += 7 {
-				if shift >= 64 {
-					return ErrIntOverflowTypes
-				}
-				if iNdEx >= l {
-					return io.ErrUnexpectedEOF
-				}
-				b := dAtA[iNdEx]
-				iNdEx++
-				stringLen |= uint64(b&0x7F) << shift
-				if b < 0x80 {
-					break
-				}
-			}
-			intStringLen := int(stringLen)
-			if intStringLen < 0 {
-				return ErrInvalidLengthTypes
-			}
-			postIndex := iNdEx + intStringLen
-			if postIndex < 0 {
-				return ErrInvalidLengthTypes
-			}
-			if postIndex > l {
-				return io.ErrUnexpectedEOF
-			}
-			m.SecondarySpAddresses = append(m.SecondarySpAddresses, string(dAtA[iNdEx:postIndex]))
-			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipTypes(dAtA[iNdEx:])
@@ -4122,6 +4086,147 @@ func (m *DeleteInfo) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
+func (m *MigrationBucketInfo) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTypes
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MigrationBucketInfo: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MigrationBucketInfo: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field SrcSpId", wireType)
+			}
+			m.SrcSpId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTypes
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.SrcSpId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field SrcGlobalVirtualGroupFamilyId", wireType)
+			}
+			m.SrcGlobalVirtualGroupFamilyId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTypes
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.SrcGlobalVirtualGroupFamilyId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 3:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field DstSpId", wireType)
+			}
+			m.DstSpId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTypes
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.DstSpId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 4:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field BucketId", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTypes
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTypes
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTypes
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.BucketId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTypes(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTypes
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
 func skipTypes(dAtA []byte) (n int, err error) {
 	l := len(dAtA)
 	iNdEx := 0
diff --git a/x/virtualgroup/client/cli/query.go b/x/virtualgroup/client/cli/query.go
new file mode 100644
index 000000000..0792156ff
--- /dev/null
+++ b/x/virtualgroup/client/cli/query.go
@@ -0,0 +1,31 @@
+package cli
+
+import (
+	"fmt"
+
+	"github.com/cosmos/cosmos-sdk/client"
+	"github.com/spf13/cobra"
+
+	"github.com/bnb-chain/greenfield/x/virtualgroup/types"
+)
+
+// GetQueryCmd returns the cli query commands for this module
+func GetQueryCmd(queryRoute string) *cobra.Command {
+	// Group storage queries under a subcommand
+	cmd := &cobra.Command{
+		Use:                        types.ModuleName,
+		Short:                      fmt.Sprintf("Querying commands for the %s module", types.ModuleName),
+		DisableFlagParsing:         true,
+		SuggestionsMinimumDistance: 2,
+		RunE:                       client.ValidateCmd,
+	}
+
+	cmd.AddCommand(CmdGlobalVirtualGroup())
+	cmd.AddCommand(CmdGlobalVirtualGroupByFamilyID())
+	cmd.AddCommand(CmdGlobalVirtualGroupFamilies())
+	cmd.AddCommand(CmdGlobalVirtualGroupFamily())
+
+	// this line is used by starport scaffolding # 1
+
+	return cmd
+}
diff --git a/x/virtualgroup/client/cli/query_global_virtual_group.go b/x/virtualgroup/client/cli/query_global_virtual_group.go
new file mode 100644
index 000000000..4f56ac2f3
--- /dev/null
+++ b/x/virtualgroup/client/cli/query_global_virtual_group.go
@@ -0,0 +1,49 @@
+package cli
+
+import (
+	"fmt"
+	"strconv"
+
+	"github.com/cosmos/cosmos-sdk/client"
+	"github.com/cosmos/cosmos-sdk/client/flags"
+	"github.com/spf13/cobra"
+
+	"github.com/bnb-chain/greenfield/x/virtualgroup/types"
+)
+
+var _ = strconv.Itoa(0)
+
+func CmdGlobalVirtualGroup() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "global-virtual-group [id]",
+		Short: "query global virtual group by its id",
+		Args:  cobra.ExactArgs(1),
+		RunE: func(cmd *cobra.Command, args []string) (err error) {
+			gvgID, err := strconv.ParseInt(args[0], 10, 32)
+			if err != nil || gvgID <= 0 {
+				return fmt.Errorf("invalid GVG id %s", args[1])
+			}
+			clientCtx, err := client.GetClientQueryContext(cmd)
+			if err != nil {
+				return err
+			}
+
+			queryClient := types.NewQueryClient(clientCtx)
+
+			params := &types.QueryGlobalVirtualGroupRequest{
+				GlobalVirtualGroupId: uint32(gvgID),
+			}
+
+			res, err := queryClient.GlobalVirtualGroup(cmd.Context(), params)
+			if err != nil {
+				return err
+			}
+
+			return clientCtx.PrintProto(res)
+		},
+	}
+
+	flags.AddQueryFlagsToCmd(cmd)
+
+	return cmd
+}
diff --git a/x/virtualgroup/client/cli/query_global_virtual_group_by_family_id.go b/x/virtualgroup/client/cli/query_global_virtual_group_by_family_id.go
new file mode 100644
index 000000000..628a1ff51
--- /dev/null
+++ b/x/virtualgroup/client/cli/query_global_virtual_group_by_family_id.go
@@ -0,0 +1,57 @@
+package cli
+
+import (
+	"fmt"
+	"strconv"
+
+	"github.com/cosmos/cosmos-sdk/client"
+	"github.com/cosmos/cosmos-sdk/client/flags"
+	"github.com/spf13/cobra"
+
+	"github.com/bnb-chain/greenfield/x/virtualgroup/types"
+)
+
+var _ = strconv.Itoa(0)
+
+func CmdGlobalVirtualGroupByFamilyID() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "global-virtual-group-by-family-id [sp-id] [family-id]",
+		Short: "query virtual group by family id",
+		Args:  cobra.ExactArgs(2),
+		RunE: func(cmd *cobra.Command, args []string) (err error) {
+
+			spID, err := strconv.ParseInt(args[0], 10, 32)
+			if err != nil || spID <= 0 {
+				return fmt.Errorf("invalid GVG id %s", args[1])
+			}
+
+			familyID, err := strconv.ParseInt(args[1], 10, 32)
+			if err != nil || familyID <= 0 {
+				return fmt.Errorf("invalid GVG id %s", args[1])
+			}
+
+			clientCtx, err := client.GetClientQueryContext(cmd)
+			if err != nil {
+				return err
+			}
+
+			queryClient := types.NewQueryClient(clientCtx)
+
+			params := &types.QueryGlobalVirtualGroupByFamilyIDRequest{
+				StorageProviderId:          uint32(spID),
+				GlobalVirtualGroupFamilyId: uint32(familyID),
+			}
+
+			res, err := queryClient.GlobalVirtualGroupByFamilyID(cmd.Context(), params)
+			if err != nil {
+				return err
+			}
+
+			return clientCtx.PrintProto(res)
+		},
+	}
+
+	flags.AddQueryFlagsToCmd(cmd)
+
+	return cmd
+}
diff --git a/x/virtualgroup/client/cli/query_global_virtual_group_families.go b/x/virtualgroup/client/cli/query_global_virtual_group_families.go
new file mode 100644
index 000000000..29415162b
--- /dev/null
+++ b/x/virtualgroup/client/cli/query_global_virtual_group_families.go
@@ -0,0 +1,57 @@
+package cli
+
+import (
+	"fmt"
+	"strconv"
+
+	"github.com/cosmos/cosmos-sdk/client"
+	"github.com/cosmos/cosmos-sdk/client/flags"
+	"github.com/cosmos/cosmos-sdk/types/query"
+	"github.com/spf13/cobra"
+
+	"github.com/bnb-chain/greenfield/x/virtualgroup/types"
+)
+
+var _ = strconv.Itoa(0)
+
+func CmdGlobalVirtualGroupFamilies() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "global-virtual-group-families [sp-id] [limit]",
+		Short: "query all global virtual groups families of the storage provider.",
+		Args:  cobra.ExactArgs(2),
+		RunE: func(cmd *cobra.Command, args []string) (err error) {
+			spID, err := strconv.ParseInt(args[0], 10, 32)
+			if err != nil || spID <= 0 {
+				return fmt.Errorf("invalid GVG id %s", args[1])
+			}
+
+			limit, err := strconv.ParseInt(args[1], 10, 32)
+			if err != nil || limit <= 0 {
+				return fmt.Errorf("invalid GVG id %s", args[1])
+			}
+
+			clientCtx, err := client.GetClientQueryContext(cmd)
+			if err != nil {
+				return err
+			}
+
+			queryClient := types.NewQueryClient(clientCtx)
+
+			params := &types.QueryGlobalVirtualGroupFamiliesRequest{
+				StorageProviderId: uint32(spID),
+				Pagination:        &query.PageRequest{Limit: uint64(limit)},
+			}
+
+			res, err := queryClient.GlobalVirtualGroupFamilies(cmd.Context(), params)
+			if err != nil {
+				return err
+			}
+
+			return clientCtx.PrintProto(res)
+		},
+	}
+
+	flags.AddQueryFlagsToCmd(cmd)
+
+	return cmd
+}
diff --git a/x/virtualgroup/client/cli/query_global_virtual_group_family.go b/x/virtualgroup/client/cli/query_global_virtual_group_family.go
new file mode 100644
index 000000000..3ac267526
--- /dev/null
+++ b/x/virtualgroup/client/cli/query_global_virtual_group_family.go
@@ -0,0 +1,55 @@
+package cli
+
+import (
+	"fmt"
+	"strconv"
+
+	"github.com/cosmos/cosmos-sdk/client"
+	"github.com/cosmos/cosmos-sdk/client/flags"
+	"github.com/spf13/cobra"
+
+	"github.com/bnb-chain/greenfield/x/virtualgroup/types"
+)
+
+var _ = strconv.Itoa(0)
+
+func CmdGlobalVirtualGroupFamily() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "global-virtual-group-family [sp-id] [family-id]",
+		Short: "query global virtual group family by its id.",
+		Args:  cobra.ExactArgs(2),
+		RunE: func(cmd *cobra.Command, args []string) (err error) {
+			spID, err := strconv.ParseInt(args[0], 10, 32)
+			if err != nil || spID <= 0 {
+				return fmt.Errorf("invalid GVG id %s", args[1])
+			}
+
+			familyID, err := strconv.ParseInt(args[1], 10, 32)
+			if err != nil || familyID <= 0 {
+				return fmt.Errorf("invalid GVG id %s", args[1])
+			}
+			clientCtx, err := client.GetClientQueryContext(cmd)
+			if err != nil {
+				return err
+			}
+
+			queryClient := types.NewQueryClient(clientCtx)
+
+			params := &types.QueryGlobalVirtualGroupFamilyRequest{
+				StorageProviderId: uint32(spID),
+				FamilyId:          uint32(familyID),
+			}
+
+			res, err := queryClient.GlobalVirtualGroupFamily(cmd.Context(), params)
+			if err != nil {
+				return err
+			}
+
+			return clientCtx.PrintProto(res)
+		},
+	}
+
+	flags.AddQueryFlagsToCmd(cmd)
+
+	return cmd
+}
diff --git a/x/virtualgroup/client/cli/tx.go b/x/virtualgroup/client/cli/tx.go
new file mode 100644
index 000000000..d1b891911
--- /dev/null
+++ b/x/virtualgroup/client/cli/tx.go
@@ -0,0 +1,72 @@
+package cli
+
+import (
+	"fmt"
+	"strconv"
+	"strings"
+
+	"github.com/cosmos/cosmos-sdk/client"
+	"github.com/cosmos/cosmos-sdk/client/flags"
+	"github.com/cosmos/cosmos-sdk/client/tx"
+	"github.com/spf13/cobra"
+
+	"github.com/bnb-chain/greenfield/x/virtualgroup/types"
+)
+
+// GetTxCmd returns the transaction commands for this module
+func GetTxCmd() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:                        types.ModuleName,
+		Short:                      fmt.Sprintf("%s transactions subcommands", types.ModuleName),
+		DisableFlagParsing:         true,
+		SuggestionsMinimumDistance: 2,
+		RunE:                       client.ValidateCmd,
+	}
+
+	cmd.AddCommand(CmdSettle())
+	// this line is used by starport scaffolding # 1
+
+	return cmd
+}
+
+func CmdSettle() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "settle",
+		Short: "Broadcast message settle",
+		Args:  cobra.ExactArgs(2),
+		RunE: func(cmd *cobra.Command, args []string) (err error) {
+			gvgFamilyId, err := strconv.ParseInt(args[0], 10, 32)
+			if err != nil || gvgFamilyId <= 0 {
+				return fmt.Errorf("invalid GVG family id %s", args[1])
+			}
+			gvgIds := make([]uint32, 0)
+			splits := strings.Split(args[1], ",")
+			for _, split := range splits {
+				gvgId, err := strconv.ParseInt(split, 10, 32)
+				if err != nil || gvgFamilyId <= 0 {
+					return fmt.Errorf("invalid GVG id %s", args[1])
+				}
+				gvgIds = append(gvgIds, uint32(gvgId))
+			}
+
+			clientCtx, err := client.GetClientTxContext(cmd)
+			if err != nil {
+				return err
+			}
+
+			msg := types.NewMsgSettle(
+				clientCtx.GetFromAddress(),
+				uint32(gvgFamilyId),
+				gvgIds,
+			)
+			if err := msg.ValidateBasic(); err != nil {
+				return err
+			}
+			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
+		},
+	}
+
+	flags.AddTxFlagsToCmd(cmd)
+
+	return cmd
+}
diff --git a/x/virtualgroup/genesis.go b/x/virtualgroup/genesis.go
new file mode 100644
index 000000000..4e0649909
--- /dev/null
+++ b/x/virtualgroup/genesis.go
@@ -0,0 +1,28 @@
+package virtualgroup
+
+import (
+	sdk "github.com/cosmos/cosmos-sdk/types"
+
+	"github.com/bnb-chain/greenfield/x/virtualgroup/keeper"
+	"github.com/bnb-chain/greenfield/x/virtualgroup/types"
+)
+
+// InitGenesis initializes the module's state from a provided genesis state.
+func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) {
+	// this line is used by starport scaffolding # genesis/module/init
+	err := k.SetParams(ctx, genState.Params)
+	if err != nil {
+		panic(err)
+	}
+	k.InitGenesis(ctx, genState)
+}
+
+// ExportGenesis returns the module's exported genesis
+func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState {
+	genesis := types.DefaultGenesis()
+	genesis.Params = k.GetParams(ctx)
+
+	// this line is used by starport scaffolding # genesis/module/export
+
+	return genesis
+}
diff --git a/x/virtualgroup/keeper/gensis.go b/x/virtualgroup/keeper/gensis.go
new file mode 100644
index 000000000..2a0895daf
--- /dev/null
+++ b/x/virtualgroup/keeper/gensis.go
@@ -0,0 +1,28 @@
+package keeper
+
+import (
+	"fmt"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+
+	"github.com/bnb-chain/greenfield/x/virtualgroup/types"
+)
+
+func (k Keeper) InitGenesis(ctx sdk.Context, genState types.GenesisState) {
+	gvgStakingPool := k.accountKeeper.GetModuleAccount(ctx, types.ModuleName)
+
+	if gvgStakingPool == nil {
+		panic(fmt.Sprintf("%s module account has not been set", types.ModuleName))
+	}
+	// if account has zero balance it probably means it's not set, so we set it
+	depositBalance := k.bankKeeper.GetAllBalances(ctx, gvgStakingPool.GetAddress())
+	if depositBalance.IsZero() {
+		k.accountKeeper.SetModuleAccount(ctx, gvgStakingPool)
+	}
+	depositAmount := sdk.ZeroInt()
+	depositCoins := sdk.NewCoins(sdk.NewCoin(genState.Params.DepositDenom, depositAmount))
+
+	if !depositBalance.IsEqual(depositCoins) {
+		panic(fmt.Sprintf("sp deposit pool balance is different from sp deposit coins: %s <-> %s", depositBalance.String(), depositCoins.String()))
+	}
+}
diff --git a/x/virtualgroup/keeper/grpc_query.go b/x/virtualgroup/keeper/grpc_query.go
new file mode 100644
index 000000000..b5a84ef3a
--- /dev/null
+++ b/x/virtualgroup/keeper/grpc_query.go
@@ -0,0 +1,116 @@
+package keeper
+
+import (
+	"context"
+
+	"github.com/cosmos/cosmos-sdk/store/prefix"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/cosmos/cosmos-sdk/types/query"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
+
+	"github.com/bnb-chain/greenfield/internal/sequence"
+	"github.com/bnb-chain/greenfield/x/virtualgroup/types"
+)
+
+func (k Keeper) Params(c context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) {
+	if req == nil {
+		return nil, status.Error(codes.InvalidArgument, "invalid request")
+	}
+	ctx := sdk.UnwrapSDKContext(c)
+
+	return &types.QueryParamsResponse{Params: k.GetParams(ctx)}, nil
+}
+
+func (k Keeper) GlobalVirtualGroup(goCtx context.Context, req *types.QueryGlobalVirtualGroupRequest) (*types.QueryGlobalVirtualGroupResponse, error) {
+	if req == nil {
+		return nil, status.Error(codes.InvalidArgument, "invalid request")
+	}
+
+	ctx := sdk.UnwrapSDKContext(goCtx)
+
+	if req == nil {
+		return nil, status.Error(codes.InvalidArgument, "invalid request")
+	}
+
+	gvg, found := k.GetGVG(ctx, req.GlobalVirtualGroupId)
+	if !found {
+		return nil, types.ErrGVGNotExist
+	}
+
+	return &types.QueryGlobalVirtualGroupResponse{GlobalVirtualGroup: gvg}, nil
+}
+
+func (k Keeper) GlobalVirtualGroupByFamilyID(goCtx context.Context, req *types.QueryGlobalVirtualGroupByFamilyIDRequest) (*types.QueryGlobalVirtualGroupByFamilyIDResponse, error) {
+	if req == nil {
+		return nil, status.Error(codes.InvalidArgument, "invalid request")
+	}
+
+	ctx := sdk.UnwrapSDKContext(goCtx)
+
+	if req == nil {
+		return nil, status.Error(codes.InvalidArgument, "invalid request")
+	}
+
+	gvgFamily, found := k.GetGVGFamily(ctx, req.StorageProviderId, req.GlobalVirtualGroupFamilyId)
+	if !found {
+		return nil, types.ErrGVGFamilyNotExist
+	}
+	var gvgs []*types.GlobalVirtualGroup
+	for _, gvgID := range gvgFamily.GlobalVirtualGroupIds {
+		gvg, found := k.GetGVG(ctx, gvgID)
+		if !found {
+			panic("gvg not found, but id exists in family")
+		}
+		gvgs = append(gvgs, gvg)
+	}
+
+	return &types.QueryGlobalVirtualGroupByFamilyIDResponse{GlobalVirtualGroups: gvgs}, nil
+}
+
+func (k Keeper) GlobalVirtualGroupFamilies(goCtx context.Context, req *types.QueryGlobalVirtualGroupFamiliesRequest) (*types.QueryGlobalVirtualGroupFamiliesResponse, error) {
+	if req == nil {
+		return nil, status.Error(codes.InvalidArgument, "invalid request")
+	}
+
+	ctx := sdk.UnwrapSDKContext(goCtx)
+
+	if req == nil {
+		return nil, status.Error(codes.InvalidArgument, "invalid request")
+	}
+
+	store := ctx.KVStore(k.storeKey)
+	var uint32Sequence sequence.Sequence[uint32]
+	gvgFamiliesStore := prefix.NewStore(store, append(types.GVGFamilyKey, uint32Sequence.EncodeSequence(req.StorageProviderId)...))
+	var gvgFamiles []*types.GlobalVirtualGroupFamily
+	pageRes, err := query.Paginate(gvgFamiliesStore, req.Pagination, func(key []byte, value []byte) error {
+		var gvgFamily types.GlobalVirtualGroupFamily
+		k.cdc.MustUnmarshal(value, &gvgFamily)
+		gvgFamiles = append(gvgFamiles, &gvgFamily)
+		return nil
+	})
+	if err != nil {
+		return nil, status.Error(codes.Internal, err.Error())
+	}
+
+	return &types.QueryGlobalVirtualGroupFamiliesResponse{GlobalVirtualGroupFamilies: gvgFamiles, Pagination: pageRes}, nil
+}
+
+func (k Keeper) GlobalVirtualGroupFamily(goCtx context.Context, req *types.QueryGlobalVirtualGroupFamilyRequest) (*types.QueryGlobalVirtualGroupFamilyResponse, error) {
+	if req == nil {
+		return nil, status.Error(codes.InvalidArgument, "invalid request")
+	}
+
+	ctx := sdk.UnwrapSDKContext(goCtx)
+
+	if req == nil {
+		return nil, status.Error(codes.InvalidArgument, "invalid request")
+	}
+
+	gvgFamily, found := k.GetGVGFamily(ctx, req.StorageProviderId, req.FamilyId)
+	if !found {
+		return nil, types.ErrGVGNotExist
+	}
+
+	return &types.QueryGlobalVirtualGroupFamilyResponse{GlobalVirtualGroupFamily: gvgFamily}, nil
+}
diff --git a/x/virtualgroup/keeper/keeper.go b/x/virtualgroup/keeper/keeper.go
new file mode 100644
index 000000000..e4edf370d
--- /dev/null
+++ b/x/virtualgroup/keeper/keeper.go
@@ -0,0 +1,570 @@
+package keeper
+
+import (
+	"encoding/binary"
+	"fmt"
+	math2 "math"
+
+	"cosmossdk.io/math"
+	"github.com/cometbft/cometbft/libs/log"
+	"github.com/cosmos/cosmos-sdk/codec"
+	"github.com/cosmos/cosmos-sdk/store/prefix"
+	storetypes "github.com/cosmos/cosmos-sdk/store/types"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/cosmos/cosmos-sdk/types/address"
+
+	"github.com/bnb-chain/greenfield/internal/sequence"
+	sptypes "github.com/bnb-chain/greenfield/x/sp/types"
+	"github.com/bnb-chain/greenfield/x/virtualgroup/types"
+)
+
+type (
+	Keeper struct {
+		cdc       codec.BinaryCodec
+		storeKey  storetypes.StoreKey
+		tStoreKey storetypes.StoreKey
+		authority string
+
+		// Keepers
+		spKeeper      types.SpKeeper
+		accountKeeper types.AccountKeeper
+		bankKeeper    types.BankKeeper
+		paymentKeeper types.PaymentKeeper
+		// sequence
+		gvgSequence       sequence.Sequence[uint32]
+		gvgFamilySequence sequence.Sequence[uint32]
+	}
+)
+
+func NewKeeper(
+	cdc codec.BinaryCodec,
+	storeKey storetypes.StoreKey,
+	tStoreKey storetypes.StoreKey,
+	authority string,
+	spKeeper types.SpKeeper,
+	accountKeeper types.AccountKeeper,
+	bankKeeper types.BankKeeper,
+	paymentKeeper types.PaymentKeeper,
+) *Keeper {
+
+	k := Keeper{
+		cdc:           cdc,
+		storeKey:      storeKey,
+		tStoreKey:     tStoreKey,
+		authority:     authority,
+		spKeeper:      spKeeper,
+		accountKeeper: accountKeeper,
+		bankKeeper:    bankKeeper,
+		paymentKeeper: paymentKeeper,
+	}
+
+	k.gvgSequence = sequence.NewSequence[uint32](types.GVGSequencePrefix)
+	k.gvgFamilySequence = sequence.NewSequence[uint32](types.GVGFamilySequencePrefix)
+
+	return &k
+}
+
+func (k Keeper) GetAuthority() string {
+	return k.authority
+}
+
+func (k Keeper) Logger(ctx sdk.Context) log.Logger {
+	return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
+}
+
+func (k Keeper) GenNextGVGID(ctx sdk.Context) uint32 {
+	store := ctx.KVStore(k.storeKey)
+
+	seq := k.gvgSequence.NextVal(store)
+	return seq
+}
+
+func (k Keeper) GenNextGVGFamilyID(ctx sdk.Context) uint32 {
+	store := ctx.KVStore(k.storeKey)
+
+	seq := k.gvgFamilySequence.NextVal(store)
+	return seq
+}
+
+func (k Keeper) SetGVG(ctx sdk.Context, gvg *types.GlobalVirtualGroup) {
+	store := ctx.KVStore(k.storeKey)
+
+	bz := k.cdc.MustMarshal(gvg)
+	store.Set(types.GetGVGKey(gvg.Id), bz)
+}
+
+func (k Keeper) DeleteGVG(ctx sdk.Context, primarySp *sptypes.StorageProvider, gvgID uint32) error {
+
+	store := ctx.KVStore(k.storeKey)
+
+	gvg, found := k.GetGVG(ctx, gvgID)
+	if !found {
+		return types.ErrGVGNotExist
+	}
+
+	if gvg.StoredSize != 0 {
+		return types.ErrGVGNotEmpty
+	}
+
+	if !k.paymentKeeper.IsEmptyNetFlow(ctx, sdk.MustAccAddressFromHex(gvg.VirtualPaymentAddress)) {
+		return types.ErrGVGNotEmpty.Wrap("The virtual payment account still not empty")
+	}
+
+	if !gvg.TotalDeposit.IsZero() {
+		// send back the deposit
+		coins := sdk.NewCoins(sdk.NewCoin(k.DepositDenomForGVG(ctx), gvg.TotalDeposit))
+		err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, sdk.MustAccAddressFromHex(primarySp.FundingAddress), coins)
+		if err != nil {
+			return err
+		}
+	}
+
+	gvgFamily, found := k.GetGVGFamily(ctx, primarySp.Id, gvg.FamilyId)
+	if !found {
+		panic("not found gvg family when delete gvg")
+	}
+
+	gvgFamily.MustRemoveGVG(gvg.Id)
+
+	for _, secondarySPID := range gvg.SecondarySpIds {
+		gvgStatisticsWithinSP := k.MustGetGVGStatisticsWithinSP(ctx, secondarySPID)
+		gvgStatisticsWithinSP.SecondaryCount--
+		k.SetGVGStatisticsWithSP(ctx, gvgStatisticsWithinSP)
+	}
+
+	store.Delete(types.GetGVGKey(gvg.Id))
+
+	if k.paymentKeeper.IsEmptyNetFlow(ctx, sdk.MustAccAddressFromHex(gvgFamily.VirtualPaymentAddress)) {
+		store.Delete(types.GetGVGFamilyKey(primarySp.Id, gvg.FamilyId))
+	} else {
+		k.SetGVGFamily(ctx, gvg.PrimarySpId, gvgFamily)
+	}
+	return nil
+}
+
+func (k Keeper) GetGVG(ctx sdk.Context, gvgID uint32) (*types.GlobalVirtualGroup, bool) {
+	store := ctx.KVStore(k.storeKey)
+
+	bz := store.Get(types.GetGVGKey(gvgID))
+	if bz == nil {
+		return nil, false
+	}
+
+	var gvg types.GlobalVirtualGroup
+	k.cdc.MustUnmarshal(bz, &gvg)
+	return &gvg, true
+}
+
+func (k Keeper) SetGVGFamily(ctx sdk.Context, primarySpID uint32, gvgFamily *types.GlobalVirtualGroupFamily) {
+
+	store := ctx.KVStore(k.storeKey)
+
+	bz := k.cdc.MustMarshal(gvgFamily)
+	store.Set(types.GetGVGFamilyKey(primarySpID, gvgFamily.Id), bz)
+}
+
+func (k Keeper) GetGVGFamily(ctx sdk.Context, spID, familyID uint32) (*types.GlobalVirtualGroupFamily, bool) {
+	store := ctx.KVStore(k.storeKey)
+
+	var gvgFamily types.GlobalVirtualGroupFamily
+	bz := store.Get(types.GetGVGFamilyKey(spID, familyID))
+	if bz == nil {
+		return nil, false
+	}
+	k.cdc.MustUnmarshal(bz, &gvgFamily)
+	return &gvgFamily, true
+}
+
+func (k Keeper) GetAndCheckGVGFamilyAvailableForNewBucket(ctx sdk.Context, spID, familyID uint32) (*types.GlobalVirtualGroupFamily, error) {
+	gvgFamily, found := k.GetGVGFamily(ctx, spID, familyID)
+	if !found {
+		return nil, types.ErrGVGFamilyNotExist
+	}
+
+	// check the maximum store size for a family
+	// If yes, no more buckets will be served
+	storeSize := k.GetStoreSizeOfFamily(ctx, gvgFamily)
+	if storeSize >= k.MaxStoreSizePerFamily(ctx) {
+		return nil, types.ErrLimitationExceed.Wrapf("The storage size within the family exceeds the limit and can't serve more buckets.. Current: %d, now: %d", k.MaxStoreSizePerFamily(ctx), storeSize)
+	}
+	return gvgFamily, nil
+}
+
+func (k Keeper) GetOrCreateEmptyGVGFamily(ctx sdk.Context, familyID uint32, spID uint32) (*types.GlobalVirtualGroupFamily, error) {
+	store := ctx.KVStore(k.storeKey)
+	var gvgFamily types.GlobalVirtualGroupFamily
+	// If familyID is not specified, a new family needs to be created
+	if familyID == types.NoSpecifiedFamilyId {
+		id := k.GenNextGVGFamilyID(ctx)
+		gvgFamily = types.GlobalVirtualGroupFamily{
+			Id:                    id,
+			VirtualPaymentAddress: k.DeriveVirtualPaymentAccount(types.GVGFamilyName, id).String(),
+		}
+		return &gvgFamily, nil
+	} else {
+		bz := store.Get(types.GetGVGFamilyKey(spID, familyID))
+		if bz == nil {
+			return nil, types.ErrGVGFamilyNotExist
+		}
+		k.cdc.MustUnmarshal(bz, &gvgFamily)
+
+		return &gvgFamily, nil
+	}
+}
+
+func (k Keeper) DeriveVirtualPaymentAccount(groupType string, id uint32) sdk.AccAddress {
+	b := make([]byte, 4)
+	binary.LittleEndian.PutUint32(b, id)
+
+	return address.Module(types.ModuleName, append([]byte(groupType), b...))
+}
+
+func (k Keeper) GetAvailableStakingTokens(ctx sdk.Context, gvg *types.GlobalVirtualGroup) math.Int {
+	stakingPrice := k.GVGStakingPerBytes(ctx)
+
+	mustStakingTokens := stakingPrice.Mul(sdk.NewInt(int64(gvg.StoredSize)))
+
+	return gvg.TotalDeposit.Sub(mustStakingTokens)
+}
+
+func (k Keeper) SwapOutAsPrimarySP(ctx sdk.Context, primarySP, successorSP *sptypes.StorageProvider, familyID uint32) error {
+	store := ctx.KVStore(k.storeKey)
+
+	family, found := k.GetGVGFamily(ctx, primarySP.Id, familyID)
+	if !found {
+		return types.ErrGVGFamilyNotExist
+	}
+	var gvgs []*types.GlobalVirtualGroup
+	for _, gvgID := range family.GlobalVirtualGroupIds {
+		gvg, found := k.GetGVG(ctx, gvgID)
+		if !found {
+			return types.ErrGVGNotExist
+		}
+		if gvg.PrimarySpId != primarySP.Id {
+			return types.ErrSwapOutFailed.Wrapf(
+				"the primary id (%d) in global virtual group is not match the primary sp id (%d)", gvg.PrimarySpId, primarySP.Id)
+		}
+		for _, spID := range gvg.SecondarySpIds {
+			if spID == successorSP.Id {
+				return types.ErrSwapOutFailed.Wrapf("the successor primary sp(ID: %d) can not be the secondary sp of gvg(%s).", successorSP.Id, gvg.String())
+			}
+		}
+
+		// swap deposit
+		if !gvg.TotalDeposit.IsZero() {
+			coins := sdk.NewCoins(sdk.NewCoin(k.DepositDenomForGVG(ctx), gvg.TotalDeposit))
+			err := k.bankKeeper.SendCoins(ctx, sdk.MustAccAddressFromHex(successorSP.FundingAddress), sdk.MustAccAddressFromHex(primarySP.FundingAddress), coins)
+			if err != nil {
+				return err
+			}
+		}
+
+		gvg.PrimarySpId = successorSP.Id
+		gvgs = append(gvgs, gvg)
+	}
+
+	// settlement
+	err := k.SettleAndDistributeGVGFamily(ctx, primarySP, family)
+	if err != nil {
+		return types.ErrSwapOutFailed.Wrapf("fail to settle GVG family %d", familyID)
+	}
+
+	k.SetGVGFamily(ctx, successorSP.Id, family)
+	for _, gvg := range gvgs {
+		k.SetGVG(ctx, gvg)
+	}
+	store.Delete(types.GetGVGFamilyKey(primarySP.Id, familyID))
+	return nil
+}
+
+func (k Keeper) SwapOutAsSecondarySP(ctx sdk.Context, secondarySP, successorSP *sptypes.StorageProvider, gvgID uint32) error {
+	gvg, found := k.GetGVG(ctx, gvgID)
+	if !found {
+		return types.ErrGVGNotExist
+	}
+	if gvg.PrimarySpId == successorSP.Id {
+		return types.ErrSwapOutFailed.Wrapf("the successor sp(ID: %d) can not be the primary sp of gvg(%s).", successorSP.Id, gvg.String())
+	}
+	secondarySPFound := false
+	secondarySPIndex := -1
+	for i, spID := range gvg.SecondarySpIds {
+		if spID == successorSP.Id {
+			return types.ErrSwapOutFailed.Wrapf("the successor sp(ID: %d) can not be one of the secondary sp of gvg(%s).", successorSP.Id, gvg.String())
+		}
+		if spID == secondarySP.Id {
+			secondarySPIndex = i
+			secondarySPFound = true
+		}
+	}
+	if !secondarySPFound {
+		return types.ErrSwapOutFailed.Wrapf("The sp(ID: %d) that needs swap out is not one of the secondary sps of gvg gvg(%s).", secondarySP.Id, gvg.String())
+	}
+	// settlement
+	err := k.SettleAndDistributeGVG(ctx, gvg)
+	if err != nil {
+		return types.ErrSwapOutFailed.Wrapf("fail to settle GVG %d", gvgID)
+	}
+
+	if secondarySPIndex == int(-1) {
+		panic("secondary sp found but the index is not correct when swap out as secondary sp")
+	}
+	gvg.SecondarySpIds[secondarySPIndex] = successorSP.Id
+	origin := k.MustGetGVGStatisticsWithinSP(ctx, secondarySP.Id)
+	successor, found := k.GetGVGStatisticsWithinSP(ctx, successorSP.Id)
+	if !found {
+		successor = &types.GVGStatisticsWithinSP{StorageProviderId: successorSP.Id}
+	}
+	origin.SecondaryCount--
+	successor.SecondaryCount++
+	k.SetGVGStatisticsWithSP(ctx, origin)
+	k.SetGVGStatisticsWithSP(ctx, successor)
+	k.SetGVG(ctx, gvg)
+	return nil
+}
+
+func (k Keeper) GetOrCreateGVGStatisticsWithinSP(ctx sdk.Context, spID uint32) *types.GVGStatisticsWithinSP {
+	store := ctx.KVStore(k.storeKey)
+
+	ret := &types.GVGStatisticsWithinSP{
+		StorageProviderId: spID,
+		SecondaryCount:    0,
+	}
+	bz := store.Get(types.GetGVGStatisticsWithinSPKey(spID))
+	if bz == nil {
+		return ret
+	}
+
+	k.cdc.MustUnmarshal(bz, ret)
+	return ret
+}
+
+func (k Keeper) GetGVGStatisticsWithinSP(ctx sdk.Context, spID uint32) (*types.GVGStatisticsWithinSP, bool) {
+	store := ctx.KVStore(k.storeKey)
+
+	bz := store.Get(types.GetGVGStatisticsWithinSPKey(spID))
+	if bz == nil {
+		return nil, false
+	}
+
+	var ret types.GVGStatisticsWithinSP
+	k.cdc.MustUnmarshal(bz, &ret)
+	return &ret, true
+}
+
+func (k Keeper) MustGetGVGStatisticsWithinSP(ctx sdk.Context, spID uint32) *types.GVGStatisticsWithinSP {
+	store := ctx.KVStore(k.storeKey)
+
+	bz := store.Get(types.GetGVGStatisticsWithinSPKey(spID))
+	if bz == nil {
+		panic("must get gvg statistics within sp")
+	}
+
+	var ret types.GVGStatisticsWithinSP
+	k.cdc.MustUnmarshal(bz, &ret)
+	return &ret
+}
+
+func (k Keeper) SetGVGStatisticsWithSP(ctx sdk.Context, gvgsStatisticsWithinSP *types.GVGStatisticsWithinSP) {
+	store := ctx.KVStore(k.storeKey)
+
+	bz := k.cdc.MustMarshal(gvgsStatisticsWithinSP)
+
+	store.Set(types.GetGVGStatisticsWithinSPKey(gvgsStatisticsWithinSP.StorageProviderId), bz)
+}
+
+func (k Keeper) BatchSetGVGStatisticsWithinSP(ctx sdk.Context, gvgsStatisticsWithinSP []*types.GVGStatisticsWithinSP) {
+	for _, g := range gvgsStatisticsWithinSP {
+		k.SetGVGStatisticsWithSP(ctx, g)
+	}
+}
+
+func (k Keeper) StorageProviderExitable(ctx sdk.Context, spID uint32) error {
+	store := ctx.KVStore(k.storeKey)
+
+	prefixStore := prefix.NewStore(store, types.GetGVGFamilyPrefixKey(spID))
+	iter := prefixStore.Iterator(nil, nil)
+	if iter.Valid() {
+		var family types.GlobalVirtualGroupFamily
+		k.cdc.MustUnmarshal(iter.Value(), &family)
+		return types.ErrSPCanNotExit.Wrapf("not swap out from all the family, key: %s", family.String())
+	}
+
+	gvgStat, found := k.GetGVGStatisticsWithinSP(ctx, spID)
+	if found && gvgStat.SecondaryCount != 0 {
+		return types.ErrSPCanNotExit.Wrapf("not swap out from all the gvgs, remain: %d", gvgStat.SecondaryCount)
+	}
+	return nil
+}
+
+// GetStoreSizeOfFamily Rather than calculating the stored size of a Global Virtual Group Family (GVGF) in real-time,
+// it is preferable to calculate it once during the creation of a Global Virtual Group (GVG). This approach is favored
+// because GVG creation is infrequent and occurs with low frequency.
+func (k Keeper) GetStoreSizeOfFamily(ctx sdk.Context, gvgFamily *types.GlobalVirtualGroupFamily) uint64 {
+	var totalStoreSize uint64
+	for _, gvgID := range gvgFamily.GlobalVirtualGroupIds {
+		gvg, found := k.GetGVG(ctx, gvgID)
+		if !found {
+			panic("gvg not found when get store size of family")
+		}
+		totalStoreSize += gvg.StoredSize
+	}
+	return totalStoreSize
+}
+
+func (k Keeper) GetTotalStakingStoreSize(ctx sdk.Context, gvg *types.GlobalVirtualGroup) uint64 {
+	total := gvg.TotalDeposit.Quo(k.GVGStakingPerBytes(ctx))
+	if !total.IsUint64() {
+		return math2.MaxUint64
+	} else {
+		return total.Uint64()
+	}
+}
+
+func (k Keeper) GetGlobalVirtualGroupIfAvailable(ctx sdk.Context, gvgID uint32, expectedStoreSize uint64) (*types.GlobalVirtualGroup, error) {
+	gvg, found := k.GetGVG(ctx, gvgID)
+	if !found {
+		return nil, types.ErrGVGNotExist
+	}
+
+	// check staking
+	if gvg.StoredSize+expectedStoreSize > k.GetTotalStakingStoreSize(ctx, gvg) {
+		return nil, types.ErrInsufficientStaking.Wrapf("gvg state: %s", gvg.String())
+	}
+	return gvg, nil
+}
+
+func (k Keeper) SetSwapOutInfo(ctx sdk.Context, gvgFamilyID uint32, gvgIDs []uint32, spID uint32, successorSPID uint32) error {
+	store := ctx.KVStore(k.storeKey)
+
+	if gvgFamilyID != types.NoSpecifiedFamilyId {
+		key := types.GetSwapOutFamilyKey(gvgFamilyID)
+		found := store.Has(key)
+		if found {
+			return types.ErrSwapOutFailed.Wrapf("SwapOutInfo of this gvg family(ID: %d) already exist", gvgFamilyID)
+
+		}
+		swapOutInfo := &types.SwapOutInfo{
+			SpId:          spID,
+			SuccessorSpId: successorSPID,
+		}
+
+		bz := k.cdc.MustMarshal(swapOutInfo)
+		store.Set(key, bz)
+	} else {
+		for _, gvgID := range gvgIDs {
+			key := types.GetSwapOutGVGKey(gvgID)
+			found := store.Has(key)
+			if found {
+				return types.ErrSwapOutFailed.Wrapf("SwapOutInfo of this gvg(ID: %d) already exist.", gvgID)
+			}
+			swapOutInfo := &types.SwapOutInfo{
+				SpId:          spID,
+				SuccessorSpId: successorSPID,
+			}
+			bz := k.cdc.MustMarshal(swapOutInfo)
+			store.Set(key, bz)
+		}
+	}
+	return nil
+}
+
+func (k Keeper) DeleteSwapOutInfo(ctx sdk.Context, gvgFamilyID uint32, gvgIDs []uint32, spID uint32) error {
+	store := ctx.KVStore(k.storeKey)
+
+	swapOutInfo := types.SwapOutInfo{}
+	if gvgFamilyID != types.NoSpecifiedFamilyId {
+		key := types.GetSwapOutFamilyKey(gvgFamilyID)
+		bz := store.Get(key)
+		k.cdc.MustUnmarshal(bz, &swapOutInfo)
+
+		if swapOutInfo.SpId != spID {
+			return sptypes.ErrStorageProviderNotFound.Wrapf("spID(%d) is different from the spID(%d) in swapOutInfo", spID, swapOutInfo.SpId)
+		} else {
+			store.Delete(key)
+		}
+
+	} else {
+		for _, gvgID := range gvgIDs {
+			key := types.GetSwapOutGVGKey(gvgID)
+			bz := store.Get(key)
+			k.cdc.MustUnmarshal(bz, &swapOutInfo)
+			if swapOutInfo.SpId != spID {
+				return sptypes.ErrStorageProviderNotFound.Wrapf("spID(%d) is different from the spID(%d) in swapOutInfo", spID, swapOutInfo.SpId)
+			} else {
+				store.Delete(key)
+			}
+		}
+	}
+
+	if err := ctx.EventManager().EmitTypedEvents(&types.EventCancelSwapOut{
+		StorageProviderId:          swapOutInfo.SpId,
+		GlobalVirtualGroupFamilyId: gvgFamilyID,
+		GlobalVirtualGroupIds:      gvgIDs,
+		SuccessorSpId:              swapOutInfo.SuccessorSpId,
+	}); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (k Keeper) CompleteSwapOut(ctx sdk.Context, gvgFamilyID uint32, gvgIDs []uint32, successorSP *sptypes.StorageProvider) error {
+	store := ctx.KVStore(k.storeKey)
+
+	swapOutInfo := types.SwapOutInfo{}
+	if gvgFamilyID != types.NoSpecifiedFamilyId {
+		key := types.GetSwapOutFamilyKey(gvgFamilyID)
+		bz := store.Get(key)
+		if bz == nil {
+			return types.ErrSwapOutFailed.Wrapf("The swap info not found in blockchain.")
+		}
+		k.cdc.MustUnmarshal(bz, &swapOutInfo)
+
+		if swapOutInfo.SuccessorSpId != successorSP.Id {
+			return types.ErrSwapOutFailed.Wrapf("The successor sp(ID: %d) is mismatch with the specify successor sp (ID: %d)", successorSP.Id, swapOutInfo.SuccessorSpId)
+		}
+
+		sp, found := k.spKeeper.GetStorageProvider(ctx, swapOutInfo.SpId)
+		if !found {
+			return sptypes.ErrStorageProviderNotFound.Wrapf("The storage provider(ID: %d) not found when complete swap out.", swapOutInfo.SpId)
+		}
+
+		err := k.SwapOutAsPrimarySP(ctx, sp, successorSP, gvgFamilyID)
+		if err != nil {
+			return err
+		}
+		store.Delete(key)
+	} else {
+		for _, gvgID := range gvgIDs {
+			key := types.GetSwapOutGVGKey(gvgID)
+			bz := store.Get(key)
+			if bz == nil {
+				return types.ErrSwapOutFailed.Wrapf("The swap info not found in blockchain.")
+			}
+			k.cdc.MustUnmarshal(bz, &swapOutInfo)
+
+			if swapOutInfo.SuccessorSpId != successorSP.Id {
+				return types.ErrSwapOutFailed.Wrapf("The successor sp(ID: %d) is mismatch with the specify successor sp (ID: %d)", successorSP.Id, swapOutInfo.SuccessorSpId)
+			}
+
+			sp, found := k.spKeeper.GetStorageProvider(ctx, swapOutInfo.SpId)
+			if !found {
+				return sptypes.ErrStorageProviderNotFound.Wrapf("The storage provider(ID: %d) not found when complete swap out.", swapOutInfo.SpId)
+			}
+
+			err := k.SwapOutAsSecondarySP(ctx, sp, successorSP, gvgID)
+			if err != nil {
+				return err
+			}
+			store.Delete(key)
+		}
+	}
+	if err := ctx.EventManager().EmitTypedEvents(&types.EventCompleteSwapOut{
+		StorageProviderId:          successorSP.Id,
+		SrcStorageProviderId:       swapOutInfo.SpId,
+		GlobalVirtualGroupFamilyId: gvgFamilyID,
+		GlobalVirtualGroupIds:      gvgIDs,
+	}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/x/virtualgroup/keeper/msg_server.go b/x/virtualgroup/keeper/msg_server.go
new file mode 100644
index 000000000..4a11bf00f
--- /dev/null
+++ b/x/virtualgroup/keeper/msg_server.go
@@ -0,0 +1,444 @@
+package keeper
+
+import (
+	"context"
+
+	sdkerrors "cosmossdk.io/errors"
+	"cosmossdk.io/math"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
+
+	gnfdtypes "github.com/bnb-chain/greenfield/types"
+	"github.com/bnb-chain/greenfield/types/errors"
+	sptypes "github.com/bnb-chain/greenfield/x/sp/types"
+	"github.com/bnb-chain/greenfield/x/virtualgroup/types"
+)
+
+type msgServer struct {
+	Keeper
+}
+
+// NewMsgServerImpl returns an implementation of the MsgServer interface
+// for the provided Keeper.
+func NewMsgServerImpl(keeper Keeper) types.MsgServer {
+	return &msgServer{Keeper: keeper}
+}
+
+var _ types.MsgServer = msgServer{}
+
+func (k msgServer) UpdateParams(goCtx context.Context, req *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error) {
+	ctx := sdk.UnwrapSDKContext(goCtx)
+	if k.GetAuthority() != req.Authority {
+		return nil, sdkerrors.Wrapf(govtypes.ErrInvalidSigner, "invalid authority; expected %s, got %s", k.GetAuthority(), req.Authority)
+	}
+
+	// Some parameters cannot be modified
+	originParams := k.GetParams(ctx)
+	if req.Params.GvgStakingPerBytes != originParams.GvgStakingPerBytes || req.Params.DepositDenom != originParams.DepositDenom {
+		return nil, errors.ErrInvalidParameter.Wrap("GvgStakingPerBytes and depositDenom are not allow to update")
+	}
+
+	if err := k.SetParams(ctx, req.Params); err != nil {
+		return nil, err
+	}
+
+	return &types.MsgUpdateParamsResponse{}, nil
+}
+
+func (k msgServer) CreateGlobalVirtualGroup(goCtx context.Context, req *types.MsgCreateGlobalVirtualGroup) (*types.MsgCreateGlobalVirtualGroupResponse, error) {
+	ctx := sdk.UnwrapSDKContext(goCtx)
+	var gvgStatisticsWithinSPs []*types.GVGStatisticsWithinSP
+
+	spOperatorAddr := sdk.MustAccAddressFromHex(req.StorageProvider)
+
+	sp, found := k.spKeeper.GetStorageProviderByOperatorAddr(ctx, spOperatorAddr)
+	if !found {
+		return nil, sptypes.ErrStorageProviderNotFound.Wrapf("The address must be operator address of sp.")
+	}
+	var secondarySpIds []uint32
+	for _, id := range req.SecondarySpIds {
+		ssp, found := k.spKeeper.GetStorageProvider(ctx, id)
+		if !found {
+			return nil, sdkerrors.Wrapf(sptypes.ErrStorageProviderNotFound, "secondary sp not found, ID: %d", id)
+		}
+		secondarySpIds = append(secondarySpIds, ssp.Id)
+		gvgStatisticsWithinSP := k.GetOrCreateGVGStatisticsWithinSP(ctx, ssp.Id)
+		gvgStatisticsWithinSP.SecondaryCount++
+		gvgStatisticsWithinSPs = append(gvgStatisticsWithinSPs, gvgStatisticsWithinSP)
+	}
+
+	gvgFamily, err := k.GetOrCreateEmptyGVGFamily(ctx, req.FamilyId, sp.Id)
+	if err != nil {
+		return nil, err
+	}
+
+	// Each family supports only a limited number of GVGS
+	if k.MaxGlobalVirtualGroupNumPerFamily(ctx) < uint32(len(gvgFamily.GlobalVirtualGroupIds)) {
+		return nil, types.ErrLimitationExceed.Wrapf("The gvg number within the family exceeds the limit.")
+	}
+
+	// deposit enough tokens for oncoming objects
+	coins := sdk.NewCoins(sdk.NewCoin(k.DepositDenomForGVG(ctx), req.Deposit.Amount))
+	err = k.bankKeeper.SendCoinsFromAccountToModule(ctx, sdk.MustAccAddressFromHex(sp.FundingAddress), types.ModuleName, coins)
+	if err != nil {
+		return nil, err
+	}
+
+	gvgID := k.GenNextGVGID(ctx)
+	gvg := &types.GlobalVirtualGroup{
+		Id:                    gvgID,
+		FamilyId:              gvgFamily.Id,
+		PrimarySpId:           sp.Id,
+		SecondarySpIds:        secondarySpIds,
+		StoredSize:            0,
+		VirtualPaymentAddress: k.DeriveVirtualPaymentAccount(types.GVGVirtualPaymentAccountName, gvgID).String(),
+		TotalDeposit:          req.Deposit.Amount,
+	}
+
+	gvgFamily.AppendGVG(gvg.Id)
+
+	k.SetGVG(ctx, gvg)
+	k.SetGVGFamily(ctx, gvg.PrimarySpId, gvgFamily)
+	k.BatchSetGVGStatisticsWithinSP(ctx, gvgStatisticsWithinSPs)
+
+	if err := ctx.EventManager().EmitTypedEvents(&types.EventCreateGlobalVirtualGroup{
+		Id:                    gvg.Id,
+		FamilyId:              gvg.FamilyId,
+		PrimarySpId:           gvg.PrimarySpId,
+		SecondarySpIds:        gvg.SecondarySpIds,
+		StoredSize:            gvg.StoredSize,
+		VirtualPaymentAddress: gvg.VirtualPaymentAddress,
+		TotalDeposit:          gvg.TotalDeposit,
+	}); err != nil {
+		return nil, err
+	}
+	if req.FamilyId == types.NoSpecifiedFamilyId {
+		if err := ctx.EventManager().EmitTypedEvents(&types.EventCreateGlobalVirtualGroupFamily{
+			Id:                    gvg.Id,
+			VirtualPaymentAddress: gvgFamily.VirtualPaymentAddress,
+		}); err != nil {
+			return nil, err
+		}
+	}
+	return &types.MsgCreateGlobalVirtualGroupResponse{}, nil
+}
+
+func (k msgServer) DeleteGlobalVirtualGroup(goCtx context.Context, req *types.MsgDeleteGlobalVirtualGroup) (*types.MsgDeleteGlobalVirtualGroupResponse, error) {
+	ctx := sdk.UnwrapSDKContext(goCtx)
+
+	spOperatorAddr := sdk.MustAccAddressFromHex(req.StorageProvider)
+
+	sp, found := k.spKeeper.GetStorageProviderByOperatorAddr(ctx, spOperatorAddr)
+	if !found {
+		return nil, sptypes.ErrStorageProviderNotFound.Wrapf("The address must be operator address of sp.")
+	}
+
+	err := k.DeleteGVG(ctx, sp, req.GlobalVirtualGroupId)
+	if err != nil {
+		return nil, err
+	}
+
+	if err = ctx.EventManager().EmitTypedEvents(&types.EventDeleteGlobalVirtualGroup{
+		Id: req.GlobalVirtualGroupId,
+	}); err != nil {
+		return nil, err
+	}
+	return &types.MsgDeleteGlobalVirtualGroupResponse{}, nil
+}
+
+func (k msgServer) Deposit(goCtx context.Context, req *types.MsgDeposit) (*types.MsgDepositResponse, error) {
+	ctx := sdk.UnwrapSDKContext(goCtx)
+
+	addr := sdk.MustAccAddressFromHex(req.StorageProvider)
+
+	var sp *sptypes.StorageProvider
+	found := false
+	sp, found = k.spKeeper.GetStorageProviderByOperatorAddr(ctx, addr)
+	if !found {
+		sp, found = k.spKeeper.GetStorageProviderByFundingAddr(ctx, addr)
+		if !found {
+			return nil, sptypes.ErrStorageProviderNotFound.Wrapf("The address must be operator/funding address of sp.")
+		}
+	}
+
+	gvg, found := k.GetGVG(ctx, req.GlobalVirtualGroupId)
+	if !found {
+		return nil, types.ErrGVGNotExist
+	}
+
+	depositDenom := k.DepositDenomForGVG(ctx)
+	if depositDenom != req.Deposit.GetDenom() {
+		return nil, sdkerrors.Wrapf(types.ErrInvalidDenom, "invalid coin denomination: got %s, expected %s", req.Deposit.Denom, depositDenom)
+	}
+
+	// deposit the deposit token to module account.
+	coins := sdk.NewCoins(sdk.NewCoin(depositDenom, req.Deposit.Amount))
+	if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, sp.GetFundingAccAddress(), types.ModuleName, coins); err != nil {
+		return nil, err
+	}
+
+	gvg.TotalDeposit = gvg.TotalDeposit.Add(req.Deposit.Amount)
+	k.SetGVG(ctx, gvg)
+
+	if err := ctx.EventManager().EmitTypedEvents(&types.EventUpdateGlobalVirtualGroup{
+		Id:           req.GlobalVirtualGroupId,
+		StoreSize:    gvg.StoredSize,
+		TotalDeposit: gvg.TotalDeposit,
+	}); err != nil {
+		return nil, err
+	}
+	return &types.MsgDepositResponse{}, nil
+}
+
+func (k msgServer) Withdraw(goCtx context.Context, req *types.MsgWithdraw) (*types.MsgWithdrawResponse, error) {
+	ctx := sdk.UnwrapSDKContext(goCtx)
+
+	addr := sdk.MustAccAddressFromHex(req.StorageProvider)
+	var sp *sptypes.StorageProvider
+	found := false
+	sp, found = k.spKeeper.GetStorageProviderByOperatorAddr(ctx, addr)
+	if !found {
+		sp, found = k.spKeeper.GetStorageProviderByFundingAddr(ctx, addr)
+		if !found {
+			return nil, sptypes.ErrStorageProviderNotFound.Wrapf("The address must be operator/funding address of sp.")
+		}
+	}
+
+	gvg, found := k.GetGVG(ctx, req.GlobalVirtualGroupId)
+	if !found {
+		return nil, types.ErrGVGNotExist
+	}
+
+	if gvg.PrimarySpId != sp.Id {
+		return nil, types.ErrWithdrawFailed.Wrapf("the withdrawer(spID: %d) is not the primary sp(ID:%d) of gvg.", sp.Id, gvg.PrimarySpId)
+	}
+
+	depositDenom := k.DepositDenomForGVG(ctx)
+	if req.Withdraw.Denom != depositDenom {
+		return nil, sdkerrors.Wrapf(types.ErrInvalidDenom, "invalid coin denomination: got %s, expected %s", req.Withdraw.Denom, k.DepositDenomForGVG(ctx))
+	}
+
+	var withdrawTokens math.Int
+
+	availableTokens := k.GetAvailableStakingTokens(ctx, gvg)
+	if availableTokens.IsNegative() {
+		panic("the available tokens is negative when withdraw")
+	}
+	if req.Withdraw.Amount.IsZero() {
+		withdrawTokens = availableTokens
+	} else {
+		if availableTokens.LT(req.Withdraw.Amount) {
+			return nil, types.ErrWithdrawAmountTooLarge
+		}
+		withdrawTokens = req.Withdraw.Amount
+	}
+
+	// withdraw the deposit token from module account to funding account.
+	coins := sdk.NewCoins(sdk.NewCoin(depositDenom, withdrawTokens))
+	if err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, sp.GetFundingAccAddress(), coins); err != nil {
+		return nil, err
+	}
+
+	if err := ctx.EventManager().EmitTypedEvents(&types.EventUpdateGlobalVirtualGroup{
+		Id:           req.GlobalVirtualGroupId,
+		StoreSize:    gvg.StoredSize,
+		TotalDeposit: gvg.TotalDeposit,
+	}); err != nil {
+		return nil, err
+	}
+
+	return &types.MsgWithdrawResponse{}, nil
+}
+
+func (k msgServer) SwapOut(goCtx context.Context, msg *types.MsgSwapOut) (*types.MsgSwapOutResponse, error) {
+	ctx := sdk.UnwrapSDKContext(goCtx)
+	operatorAddr := sdk.MustAccAddressFromHex(msg.StorageProvider)
+	sp, found := k.spKeeper.GetStorageProviderByOperatorAddr(ctx, operatorAddr)
+	if !found {
+		return nil, sptypes.ErrStorageProviderNotFound.Wrapf("The address must be operator/funding address of sp.")
+	}
+
+	successorSP, found := k.spKeeper.GetStorageProvider(ctx, msg.SuccessorSpId)
+	if !found {
+		return nil, sptypes.ErrStorageProviderNotFound.Wrapf("successor sp not found.")
+	}
+
+	// verify the approval
+	err := gnfdtypes.VerifySignature(sdk.MustAccAddressFromHex(successorSP.ApprovalAddress), sdk.Keccak256(msg.GetApprovalBytes()), msg.SuccessorSpApproval.Sig)
+	if err != nil {
+		return nil, err
+	}
+
+	err = k.SetSwapOutInfo(ctx, msg.GlobalVirtualGroupFamilyId, msg.GlobalVirtualGroupIds, sp.Id, successorSP.Id)
+	if err != nil {
+		return nil, err
+	}
+
+	if err = ctx.EventManager().EmitTypedEvents(&types.EventSwapOut{
+		StorageProviderId:          sp.Id,
+		GlobalVirtualGroupFamilyId: msg.GlobalVirtualGroupFamilyId,
+		GlobalVirtualGroupIds:      msg.GlobalVirtualGroupIds,
+		SuccessorSpId:              successorSP.Id,
+	}); err != nil {
+		return nil, err
+	}
+	return &types.MsgSwapOutResponse{}, nil
+}
+
+func (k msgServer) CancelSwapOut(goCtx context.Context, msg *types.MsgCancelSwapOut) (*types.MsgCancelSwapOutResponse, error) {
+	ctx := sdk.UnwrapSDKContext(goCtx)
+
+	operatorAddr := sdk.MustAccAddressFromHex(msg.StorageProvider)
+	sp, found := k.spKeeper.GetStorageProviderByOperatorAddr(ctx, operatorAddr)
+	if !found {
+		return nil, sptypes.ErrStorageProviderNotFound.Wrapf("The address must be operator/funding address of sp.")
+	}
+
+	err := k.DeleteSwapOutInfo(ctx, msg.GlobalVirtualGroupFamilyId, msg.GlobalVirtualGroupIds, sp.Id)
+	if err != nil {
+		return nil, err
+	}
+
+	return &types.MsgCancelSwapOutResponse{}, nil
+}
+
+func (k msgServer) CompleteSwapOut(goCtx context.Context, msg *types.MsgCompleteSwapOut) (*types.MsgCompleteSwapOutResponse, error) {
+	ctx := sdk.UnwrapSDKContext(goCtx)
+
+	operatorAddr := sdk.MustAccAddressFromHex(msg.StorageProvider)
+	sp, found := k.spKeeper.GetStorageProviderByOperatorAddr(ctx, operatorAddr)
+	if !found {
+		return nil, sptypes.ErrStorageProviderNotFound.Wrapf("The address must be operator/funding address of sp.")
+	}
+
+	err := k.Keeper.CompleteSwapOut(ctx, msg.GlobalVirtualGroupFamilyId, msg.GlobalVirtualGroupIds, sp)
+	if err != nil {
+		return nil, err
+	}
+
+	return &types.MsgCompleteSwapOutResponse{}, nil
+}
+
+func (k msgServer) Settle(goCtx context.Context, req *types.MsgSettle) (*types.MsgSettleResponse, error) {
+	ctx := sdk.UnwrapSDKContext(goCtx)
+
+	addr := sdk.MustAccAddressFromHex(req.StorageProvider)
+	var sp *sptypes.StorageProvider
+	found := false
+	sp, found = k.spKeeper.GetStorageProviderByOperatorAddr(ctx, addr)
+	if !found {
+		sp, found = k.spKeeper.GetStorageProviderByFundingAddr(ctx, addr)
+		if !found {
+			return nil, sptypes.ErrStorageProviderNotFound.Wrapf("The address must be operator/funding address of sp.")
+		}
+	}
+
+	if req.GlobalVirtualGroupFamilyId != types.NoSpecifiedFamilyId {
+		family, found := k.GetGVGFamily(ctx, sp.Id, req.GlobalVirtualGroupFamilyId)
+		if !found {
+			return nil, types.ErrGVGFamilyNotExist
+		}
+
+		err := k.SettleAndDistributeGVGFamily(ctx, sp, family)
+		if err != nil {
+			return nil, types.ErrSettleFailed
+		}
+	} else {
+		m := make(map[uint32]struct{})
+		for _, gvgID := range req.GlobalVirtualGroupIds {
+			m[gvgID] = struct{}{}
+		}
+		for gvgID := range m {
+			gvg, found := k.GetGVG(ctx, gvgID)
+			if !found {
+				return nil, types.ErrGVGNotExist
+			}
+
+			permitted := false
+			for _, id := range gvg.SecondarySpIds {
+				if id == sp.Id {
+					permitted = true
+					break
+				}
+			}
+			if !permitted {
+				return nil, sdkerrors.Wrapf(types.ErrSettleFailed, "storage provider %d is not in the group", sp.Id)
+			}
+
+			err := k.SettleAndDistributeGVG(ctx, gvg)
+			if err != nil {
+				return nil, types.ErrSettleFailed
+			}
+		}
+	}
+
+	return &types.MsgSettleResponse{}, nil
+}
+
+func (k msgServer) StorageProviderExit(goCtx context.Context, msg *types.MsgStorageProviderExit) (*types.MsgStorageProviderExitResponse, error) {
+	ctx := sdk.UnwrapSDKContext(goCtx)
+
+	operatorAddr := sdk.MustAccAddressFromHex(msg.StorageProvider)
+
+	sp, found := k.spKeeper.GetStorageProviderByOperatorAddr(ctx, operatorAddr)
+	if !found {
+		return nil, sptypes.ErrStorageProviderNotFound.Wrapf("The address must be operator address of sp.")
+	}
+
+	if sp.Status != sptypes.STATUS_IN_SERVICE {
+		return nil, sptypes.ErrStorageProviderExitFailed.Wrapf("sp not in service, status: %s", sp.Status.String())
+	}
+
+	sp.Status = sptypes.STATUS_GRACEFUL_EXITING
+
+	k.spKeeper.SetStorageProvider(ctx, sp)
+
+	if err := ctx.EventManager().EmitTypedEvents(&types.EventStorageProviderExit{
+		StorageProviderId: sp.Id,
+		OperatorAddress:   sp.OperatorAddress,
+	}); err != nil {
+		return nil, err
+	}
+	return &types.MsgStorageProviderExitResponse{}, nil
+}
+
+func (k msgServer) CompleteStorageProviderExit(goCtx context.Context, msg *types.MsgCompleteStorageProviderExit) (*types.MsgCompleteStorageProviderExitResponse, error) {
+	ctx := sdk.UnwrapSDKContext(goCtx)
+
+	operatorAddr := sdk.MustAccAddressFromHex(msg.StorageProvider)
+
+	sp, found := k.spKeeper.GetStorageProviderByOperatorAddr(ctx, operatorAddr)
+	if !found {
+		return nil, sptypes.ErrStorageProviderNotFound.Wrapf("The address must be operator address of sp.")
+	}
+
+	if sp.Status != sptypes.STATUS_GRACEFUL_EXITING {
+		return nil, sptypes.ErrStorageProviderExitFailed.Wrapf(
+			"sp(id : %d, operator address: %s) not in the process of exiting", sp.Id, sp.OperatorAddress)
+	}
+
+	err := k.StorageProviderExitable(ctx, sp.Id)
+	if err != nil {
+		return nil, err
+	}
+
+	// send back the total deposit
+	coins := sdk.NewCoins(sdk.NewCoin(k.spKeeper.DepositDenomForSP(ctx), sp.TotalDeposit))
+	err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, sptypes.ModuleName, sdk.MustAccAddressFromHex(sp.FundingAddress), coins)
+	if err != nil {
+		return nil, err
+	}
+
+	err = k.spKeeper.Exit(ctx, sp)
+	if err != nil {
+		return nil, err
+	}
+	if err := ctx.EventManager().EmitTypedEvents(&types.EventCompleteStorageProviderExit{
+		StorageProviderId: sp.Id,
+		OperatorAddress:   sp.OperatorAddress,
+		TotalDeposit:      sp.TotalDeposit,
+	}); err != nil {
+		return nil, err
+	}
+	return &types.MsgCompleteStorageProviderExitResponse{}, nil
+}
diff --git a/x/virtualgroup/keeper/params.go b/x/virtualgroup/keeper/params.go
new file mode 100644
index 000000000..0fed21384
--- /dev/null
+++ b/x/virtualgroup/keeper/params.go
@@ -0,0 +1,53 @@
+package keeper
+
+import (
+	"cosmossdk.io/math"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+
+	"github.com/bnb-chain/greenfield/x/virtualgroup/types"
+)
+
+func (k Keeper) DepositDenomForGVG(ctx sdk.Context) (res string) {
+	params := k.GetParams(ctx)
+	return params.DepositDenom
+}
+
+func (k Keeper) GVGStakingPerBytes(ctx sdk.Context) (res math.Int) {
+	params := k.GetParams(ctx)
+	return params.GvgStakingPerBytes
+}
+
+func (k Keeper) MaxGlobalVirtualGroupNumPerFamily(ctx sdk.Context) (res uint32) {
+	params := k.GetParams(ctx)
+	return params.MaxGlobalVirtualGroupNumPerFamily
+}
+
+func (k Keeper) MaxStoreSizePerFamily(ctx sdk.Context) (res uint64) {
+	params := k.GetParams(ctx)
+	return params.MaxStoreSizePerFamily
+}
+
+// GetParams returns the current sp module parameters.
+func (k Keeper) GetParams(ctx sdk.Context) (p types.Params) {
+	store := ctx.KVStore(k.storeKey)
+	bz := store.Get(types.ParamsKey)
+	if bz == nil {
+		return p
+	}
+
+	k.cdc.MustUnmarshal(bz, &p)
+	return p
+}
+
+// SetParams sets the params of sp module
+func (k Keeper) SetParams(ctx sdk.Context, params types.Params) error {
+	if err := params.Validate(); err != nil {
+		return err
+	}
+
+	store := ctx.KVStore(k.storeKey)
+	bz := k.cdc.MustMarshal(&params)
+	store.Set(types.ParamsKey, bz)
+
+	return nil
+}
diff --git a/x/virtualgroup/keeper/payment.go b/x/virtualgroup/keeper/payment.go
new file mode 100644
index 000000000..2539d1344
--- /dev/null
+++ b/x/virtualgroup/keeper/payment.go
@@ -0,0 +1,52 @@
+package keeper
+
+import (
+	"fmt"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+
+	sptypes "github.com/bnb-chain/greenfield/x/sp/types"
+	"github.com/bnb-chain/greenfield/x/virtualgroup/types"
+)
+
+func (k Keeper) SettleAndDistributeGVGFamily(ctx sdk.Context, sp *sptypes.StorageProvider, family *types.GlobalVirtualGroupFamily) error {
+	paymentAddress := sdk.MustAccAddressFromHex(family.GetVirtualPaymentAddress())
+	totalBalance, err := k.paymentKeeper.QueryDynamicBalance(ctx, paymentAddress)
+	if err != nil {
+		return fmt.Errorf("fail to query balance: %s, err: %s", paymentAddress.String(), err.Error())
+	}
+	if !totalBalance.IsPositive() {
+		return nil
+	}
+
+	err = k.paymentKeeper.Withdraw(ctx, paymentAddress, sdk.MustAccAddressFromHex(sp.FundingAddress), totalBalance)
+	if err != nil {
+		return fmt.Errorf("fail to send coins: %s %s", paymentAddress, sp.FundingAddress)
+	}
+
+	return nil
+}
+
+func (k Keeper) SettleAndDistributeGVG(ctx sdk.Context, gvg *types.GlobalVirtualGroup) error {
+	paymentAddress := sdk.MustAccAddressFromHex(gvg.GetVirtualPaymentAddress())
+	totalBalance, err := k.paymentKeeper.QueryDynamicBalance(ctx, paymentAddress)
+	if err != nil {
+		return fmt.Errorf("fail to query balance: %s, err: %s", paymentAddress.String(), err.Error())
+	}
+
+	amount := totalBalance.QuoRaw(int64(len(gvg.SecondarySpIds)))
+	if !amount.IsPositive() {
+		return nil
+	}
+	for _, spID := range gvg.SecondarySpIds {
+		sp, found := k.spKeeper.GetStorageProvider(ctx, spID)
+		if !found {
+			return fmt.Errorf("fail to find secondary sp: %d", spID)
+		}
+		err = k.paymentKeeper.Withdraw(ctx, paymentAddress, sdk.MustAccAddressFromHex(sp.FundingAddress), amount)
+		if err != nil {
+			return fmt.Errorf("fail to send coins: %s %s", paymentAddress, sp.FundingAddress)
+		}
+	}
+	return nil
+}
diff --git a/x/virtualgroup/module.go b/x/virtualgroup/module.go
new file mode 100644
index 000000000..9afdd6832
--- /dev/null
+++ b/x/virtualgroup/module.go
@@ -0,0 +1,148 @@
+package virtualgroup
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+
+	abci "github.com/cometbft/cometbft/abci/types"
+	"github.com/cosmos/cosmos-sdk/client"
+	"github.com/cosmos/cosmos-sdk/codec"
+	cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/cosmos/cosmos-sdk/types/module"
+	"github.com/grpc-ecosystem/grpc-gateway/runtime"
+	"github.com/spf13/cobra"
+
+	"github.com/bnb-chain/greenfield/x/virtualgroup/client/cli"
+	"github.com/bnb-chain/greenfield/x/virtualgroup/keeper"
+	"github.com/bnb-chain/greenfield/x/virtualgroup/types"
+)
+
+var (
+	_ module.AppModule      = AppModule{}
+	_ module.AppModuleBasic = AppModuleBasic{}
+)
+
+// ----------------------------------------------------------------------------
+// AppModuleBasic
+// ----------------------------------------------------------------------------
+
+// AppModuleBasic implements the AppModuleBasic interface that defines the independent methods a Cosmos SDK module needs to implement.
+type AppModuleBasic struct {
+	cdc codec.BinaryCodec
+}
+
+func NewAppModuleBasic(cdc codec.BinaryCodec) AppModuleBasic {
+	return AppModuleBasic{cdc: cdc}
+}
+
+// Name returns the name of the module as a string
+func (AppModuleBasic) Name() string {
+	return types.ModuleName
+}
+
+// RegisterLegacyAminoCodec registers the amino codec for the module, which is used to marshal and unmarshal structs to/from []byte in order to persist them in the module's KVStore
+func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
+	types.RegisterCodec(cdc)
+}
+
+// RegisterInterfaces registers a module's interface types and their concrete implementations as proto.Message
+func (a AppModuleBasic) RegisterInterfaces(reg cdctypes.InterfaceRegistry) {
+	types.RegisterInterfaces(reg)
+}
+
+// DefaultGenesis returns a default GenesisState for the module, marshalled to json.RawMessage. The default GenesisState need to be defined by the module developer and is primarily used for testing
+func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage {
+	return cdc.MustMarshalJSON(types.DefaultGenesis())
+}
+
+// ValidateGenesis used to validate the GenesisState, given in its json.RawMessage form
+func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage) error {
+	var genState types.GenesisState
+	if err := cdc.UnmarshalJSON(bz, &genState); err != nil {
+		return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err)
+	}
+	return genState.Validate()
+}
+
+// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the module
+func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) {
+	err := types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx))
+	if err != nil {
+		return
+	}
+}
+
+// GetTxCmd returns the root Tx command for the module. The subcommands of this root command are used by end-users to generate new transactions containing messages defined in the module
+func (a AppModuleBasic) GetTxCmd() *cobra.Command {
+	return cli.GetTxCmd()
+}
+
+// GetQueryCmd returns the root query command for the module. The subcommands of this root command are used by end-users to generate new queries to the subset of the state defined by the module
+func (AppModuleBasic) GetQueryCmd() *cobra.Command {
+	return cli.GetQueryCmd(types.StoreKey)
+}
+
+// ----------------------------------------------------------------------------
+// AppModule
+// ----------------------------------------------------------------------------
+
+// AppModule implements the AppModule interface that defines the inter-dependent methods that modules need to implement
+type AppModule struct {
+	AppModuleBasic
+
+	keeper        keeper.Keeper
+	bankKeeper    types.BankKeeper
+	accountKeeper types.AccountKeeper
+	spKeeper      types.SpKeeper
+}
+
+func NewAppModule(
+	cdc codec.Codec,
+	keeper keeper.Keeper,
+	spKeeper types.SpKeeper,
+) AppModule {
+	return AppModule{
+		AppModuleBasic: NewAppModuleBasic(cdc),
+		keeper:         keeper,
+		spKeeper:       spKeeper,
+	}
+}
+
+// RegisterServices registers a gRPC query service to respond to the module-specific gRPC queries
+func (am AppModule) RegisterServices(cfg module.Configurator) {
+	types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper))
+	types.RegisterQueryServer(cfg.QueryServer(), am.keeper)
+}
+
+// RegisterInvariants registers the invariants of the module. If an invariant deviates from its predicted value, the InvariantRegistry triggers appropriate logic (most often the chain will be halted)
+func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {}
+
+// InitGenesis performs the module's genesis initialization. It returns no validator updates.
+func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) []abci.ValidatorUpdate {
+	var genState types.GenesisState
+	// Initialize global index to index in genesis state
+	cdc.MustUnmarshalJSON(gs, &genState)
+
+	InitGenesis(ctx, am.keeper, genState)
+
+	return []abci.ValidatorUpdate{}
+}
+
+// ExportGenesis returns the module's exported genesis state as raw JSON bytes.
+func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage {
+	genState := ExportGenesis(ctx, am.keeper)
+	return cdc.MustMarshalJSON(genState)
+}
+
+// ConsensusVersion is a sequence number for state-breaking change of the module. It should be incremented on each consensus-breaking change introduced by the module. To avoid wrong/empty versions, the initial version should be set to 1
+func (AppModule) ConsensusVersion() uint64 { return 1 }
+
+// BeginBlock contains the logic that is automatically triggered at the beginning of each block
+func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {}
+
+// EndBlock contains the logic that is automatically triggered at the end of each block
+func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate {
+	return []abci.ValidatorUpdate{}
+}
diff --git a/x/virtualgroup/module_simulation.go b/x/virtualgroup/module_simulation.go
new file mode 100644
index 000000000..44168c0aa
--- /dev/null
+++ b/x/virtualgroup/module_simulation.go
@@ -0,0 +1,157 @@
+package virtualgroup
+
+import (
+	"math/rand"
+
+	"github.com/cosmos/cosmos-sdk/baseapp"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/cosmos/cosmos-sdk/types/module"
+	simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
+	"github.com/cosmos/cosmos-sdk/x/simulation"
+
+	"github.com/bnb-chain/greenfield/testutil/sample"
+	virtualgroupsimulation "github.com/bnb-chain/greenfield/x/virtualgroup/simulation"
+	"github.com/bnb-chain/greenfield/x/virtualgroup/types"
+)
+
+// avoid unused import issue
+var (
+	_ = sample.AccAddress
+	_ = virtualgroupsimulation.FindAccount
+	_ = simulation.MsgEntryKind
+	_ = baseapp.Paramspace
+	_ = rand.Rand{}
+)
+
+const (
+	opWeightMsgStorageProviderExit = "op_weight_msg_storage_provider_exit"
+	// TODO: Determine the simulation weight value
+	defaultWeightMsgStorageProviderExit int = 100
+
+	opWeightMsgCompleteStorageProviderExit = "op_weight_msg_complete_storage_provider_exit"
+	// TODO: Determine the simulation weight value
+	defaultWeightMsgCompleteStorageProviderExit int = 100
+
+	opWeightMsgCompleteSwapOut = "op_weight_msg_complete_swap_out"
+	// TODO: Determine the simulation weight value
+	defaultWeightMsgCompleteSwapOut int = 100
+
+	opWeightMsgCancelSwapOut = "op_weight_msg_cancel_swap_out"
+	// TODO: Determine the simulation weight value
+	defaultWeightMsgCancelSwapOut int = 100
+
+	// this line is used by starport scaffolding # simapp/module/const
+)
+
+// GenerateGenesisState creates a randomized GenState of the module.
+func (AppModule) GenerateGenesisState(simState *module.SimulationState) {
+	accs := make([]string, len(simState.Accounts))
+	for i, acc := range simState.Accounts {
+		accs[i] = acc.Address.String()
+	}
+	virtualgroupGenesis := types.GenesisState{
+		Params: types.DefaultParams(),
+		// this line is used by starport scaffolding # simapp/module/genesisState
+	}
+	simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(&virtualgroupGenesis)
+}
+
+// RegisterStoreDecoder registers a decoder.
+func (am AppModule) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) {}
+
+// ProposalContents doesn't return any content functions for governance proposals.
+func (AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalMsg {
+	return nil
+}
+
+// WeightedOperations returns the all the gov module operations with their respective weights.
+func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation {
+	operations := make([]simtypes.WeightedOperation, 0)
+
+	var weightMsgStorageProviderExit int
+	simState.AppParams.GetOrGenerate(simState.Cdc, opWeightMsgStorageProviderExit, &weightMsgStorageProviderExit, nil,
+		func(_ *rand.Rand) {
+			weightMsgStorageProviderExit = defaultWeightMsgStorageProviderExit
+		},
+	)
+	operations = append(operations, simulation.NewWeightedOperation(
+		weightMsgStorageProviderExit,
+		virtualgroupsimulation.SimulateMsgStorageProviderExit(am.accountKeeper, am.bankKeeper, am.keeper),
+	))
+
+	var weightMsgCompleteStorageProviderExit int
+	simState.AppParams.GetOrGenerate(simState.Cdc, opWeightMsgCompleteStorageProviderExit, &weightMsgCompleteStorageProviderExit, nil,
+		func(_ *rand.Rand) {
+			weightMsgCompleteStorageProviderExit = defaultWeightMsgCompleteStorageProviderExit
+		},
+	)
+	operations = append(operations, simulation.NewWeightedOperation(
+		weightMsgCompleteStorageProviderExit,
+		virtualgroupsimulation.SimulateMsgCompleteStorageProviderExit(am.accountKeeper, am.bankKeeper, am.keeper),
+	))
+
+	var weightMsgCompleteSwapOut int
+	simState.AppParams.GetOrGenerate(simState.Cdc, opWeightMsgCompleteSwapOut, &weightMsgCompleteSwapOut, nil,
+		func(_ *rand.Rand) {
+			weightMsgCompleteSwapOut = defaultWeightMsgCompleteSwapOut
+		},
+	)
+	operations = append(operations, simulation.NewWeightedOperation(
+		weightMsgCompleteSwapOut,
+		virtualgroupsimulation.SimulateMsgCompleteSwapOut(am.accountKeeper, am.bankKeeper, am.keeper),
+	))
+
+	var weightMsgCancelSwapOut int
+	simState.AppParams.GetOrGenerate(simState.Cdc, opWeightMsgCancelSwapOut, &weightMsgCancelSwapOut, nil,
+		func(_ *rand.Rand) {
+			weightMsgCancelSwapOut = defaultWeightMsgCancelSwapOut
+		},
+	)
+	operations = append(operations, simulation.NewWeightedOperation(
+		weightMsgCancelSwapOut,
+		virtualgroupsimulation.SimulateMsgCancelSwapOut(am.accountKeeper, am.bankKeeper, am.keeper),
+	))
+
+	// this line is used by starport scaffolding # simapp/module/operation
+
+	return operations
+}
+
+// ProposalMsgs returns msgs used for governance proposals for simulations.
+func (am AppModule) ProposalMsgs(simState module.SimulationState) []simtypes.WeightedProposalMsg {
+	return []simtypes.WeightedProposalMsg{
+		simulation.NewWeightedProposalMsg(
+			opWeightMsgStorageProviderExit,
+			defaultWeightMsgStorageProviderExit,
+			func(r *rand.Rand, ctx sdk.Context, accs []simtypes.Account) sdk.Msg {
+				virtualgroupsimulation.SimulateMsgStorageProviderExit(am.accountKeeper, am.bankKeeper, am.keeper)
+				return nil
+			},
+		),
+		simulation.NewWeightedProposalMsg(
+			opWeightMsgCompleteStorageProviderExit,
+			defaultWeightMsgCompleteStorageProviderExit,
+			func(r *rand.Rand, ctx sdk.Context, accs []simtypes.Account) sdk.Msg {
+				virtualgroupsimulation.SimulateMsgCompleteStorageProviderExit(am.accountKeeper, am.bankKeeper, am.keeper)
+				return nil
+			},
+		),
+		simulation.NewWeightedProposalMsg(
+			opWeightMsgCompleteSwapOut,
+			defaultWeightMsgCompleteSwapOut,
+			func(r *rand.Rand, ctx sdk.Context, accs []simtypes.Account) sdk.Msg {
+				virtualgroupsimulation.SimulateMsgCompleteSwapOut(am.accountKeeper, am.bankKeeper, am.keeper)
+				return nil
+			},
+		),
+		simulation.NewWeightedProposalMsg(
+			opWeightMsgCancelSwapOut,
+			defaultWeightMsgCancelSwapOut,
+			func(r *rand.Rand, ctx sdk.Context, accs []simtypes.Account) sdk.Msg {
+				virtualgroupsimulation.SimulateMsgCancelSwapOut(am.accountKeeper, am.bankKeeper, am.keeper)
+				return nil
+			},
+		),
+		// this line is used by starport scaffolding # simapp/module/OpMsg
+	}
+}
diff --git a/x/virtualgroup/simulation/cancel_swap_out.go b/x/virtualgroup/simulation/cancel_swap_out.go
new file mode 100644
index 000000000..8e6b3d1c3
--- /dev/null
+++ b/x/virtualgroup/simulation/cancel_swap_out.go
@@ -0,0 +1,30 @@
+package simulation
+
+import (
+	"math/rand"
+
+	"github.com/cosmos/cosmos-sdk/baseapp"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
+
+	"github.com/bnb-chain/greenfield/x/virtualgroup/keeper"
+	"github.com/bnb-chain/greenfield/x/virtualgroup/types"
+)
+
+func SimulateMsgCancelSwapOut(
+	ak types.AccountKeeper,
+	bk types.BankKeeper,
+	k keeper.Keeper,
+) simtypes.Operation {
+	return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
+	) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
+		simAccount, _ := simtypes.RandomAcc(r, accs)
+		msg := &types.MsgCancelSwapOut{
+			StorageProvider: simAccount.Address.String(),
+		}
+
+		// TODO: Handling the CancelSwapOut simulation
+
+		return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "CancelSwapOut simulation not implemented"), nil, nil
+	}
+}
diff --git a/x/virtualgroup/simulation/complete_storage_provider_exit.go b/x/virtualgroup/simulation/complete_storage_provider_exit.go
new file mode 100644
index 000000000..792a7d065
--- /dev/null
+++ b/x/virtualgroup/simulation/complete_storage_provider_exit.go
@@ -0,0 +1,30 @@
+package simulation
+
+import (
+	"math/rand"
+
+	"github.com/cosmos/cosmos-sdk/baseapp"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
+
+	"github.com/bnb-chain/greenfield/x/virtualgroup/keeper"
+	"github.com/bnb-chain/greenfield/x/virtualgroup/types"
+)
+
+func SimulateMsgCompleteStorageProviderExit(
+	ak types.AccountKeeper,
+	bk types.BankKeeper,
+	k keeper.Keeper,
+) simtypes.Operation {
+	return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
+	) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
+		simAccount, _ := simtypes.RandomAcc(r, accs)
+		msg := &types.MsgCompleteStorageProviderExit{
+			StorageProvider: simAccount.Address.String(),
+		}
+
+		// TODO: Handling the CompleteStorageProviderExit simulation
+
+		return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "CompleteStorageProviderExit simulation not implemented"), nil, nil
+	}
+}
diff --git a/x/virtualgroup/simulation/complete_swap_out.go b/x/virtualgroup/simulation/complete_swap_out.go
new file mode 100644
index 000000000..501b0649f
--- /dev/null
+++ b/x/virtualgroup/simulation/complete_swap_out.go
@@ -0,0 +1,30 @@
+package simulation
+
+import (
+	"math/rand"
+
+	"github.com/cosmos/cosmos-sdk/baseapp"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
+
+	"github.com/bnb-chain/greenfield/x/virtualgroup/keeper"
+	"github.com/bnb-chain/greenfield/x/virtualgroup/types"
+)
+
+func SimulateMsgCompleteSwapOut(
+	ak types.AccountKeeper,
+	bk types.BankKeeper,
+	k keeper.Keeper,
+) simtypes.Operation {
+	return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
+	) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
+		simAccount, _ := simtypes.RandomAcc(r, accs)
+		msg := &types.MsgCompleteSwapOut{
+			StorageProvider: simAccount.Address.String(),
+		}
+
+		// TODO: Handling the CompleteSwapOut simulation
+
+		return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "CompleteSwapOut simulation not implemented"), nil, nil
+	}
+}
diff --git a/x/virtualgroup/simulation/helpers.go b/x/virtualgroup/simulation/helpers.go
new file mode 100644
index 000000000..ecdeee817
--- /dev/null
+++ b/x/virtualgroup/simulation/helpers.go
@@ -0,0 +1,15 @@
+package simulation
+
+import (
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
+)
+
+// FindAccount find a specific address from an account list
+func FindAccount(accs []simtypes.Account, address string) (simtypes.Account, bool) {
+	creator, err := sdk.AccAddressFromHexUnsafe(address)
+	if err != nil {
+		panic(err)
+	}
+	return simtypes.FindAccount(accs, creator)
+}
diff --git a/x/virtualgroup/simulation/storage_provider_exit.go b/x/virtualgroup/simulation/storage_provider_exit.go
new file mode 100644
index 000000000..97f78ee97
--- /dev/null
+++ b/x/virtualgroup/simulation/storage_provider_exit.go
@@ -0,0 +1,30 @@
+package simulation
+
+import (
+	"math/rand"
+
+	"github.com/cosmos/cosmos-sdk/baseapp"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
+
+	"github.com/bnb-chain/greenfield/x/virtualgroup/keeper"
+	"github.com/bnb-chain/greenfield/x/virtualgroup/types"
+)
+
+func SimulateMsgStorageProviderExit(
+	ak types.AccountKeeper,
+	bk types.BankKeeper,
+	k keeper.Keeper,
+) simtypes.Operation {
+	return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
+	) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
+		simAccount, _ := simtypes.RandomAcc(r, accs)
+		msg := &types.MsgStorageProviderExit{
+			StorageProvider: simAccount.Address.String(),
+		}
+
+		// TODO: Handling the StorageProviderExit simulation
+
+		return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "StorageProviderExit simulation not implemented"), nil, nil
+	}
+}
diff --git a/x/virtualgroup/types/codec.go b/x/virtualgroup/types/codec.go
new file mode 100644
index 000000000..903cac2ac
--- /dev/null
+++ b/x/virtualgroup/types/codec.go
@@ -0,0 +1,60 @@
+package types
+
+import (
+	"github.com/cosmos/cosmos-sdk/codec"
+	cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/cosmos/cosmos-sdk/types/msgservice"
+)
+
+func RegisterCodec(cdc *codec.LegacyAmino) {
+	cdc.RegisterConcrete(&MsgStorageProviderExit{}, "virtualgroup/StorageProviderExit", nil)
+	cdc.RegisterConcrete(&MsgCompleteStorageProviderExit{}, "virtualgroup/CompleteStorageProviderExit", nil)
+	cdc.RegisterConcrete(&MsgCompleteSwapOut{}, "virtualgroup/CompleteSwapOut", nil)
+	cdc.RegisterConcrete(&MsgCancelSwapOut{}, "virtualgroup/CancelSwapOut", nil)
+	// this line is used by starport scaffolding # 2
+}
+
+func RegisterInterfaces(registry cdctypes.InterfaceRegistry) {
+	registry.RegisterImplementations((*sdk.Msg)(nil),
+		&MsgCreateGlobalVirtualGroup{},
+	)
+	registry.RegisterImplementations((*sdk.Msg)(nil),
+		&MsgDeleteGlobalVirtualGroup{},
+	)
+	registry.RegisterImplementations((*sdk.Msg)(nil),
+		&MsgStorageProviderExit{},
+	)
+	registry.RegisterImplementations((*sdk.Msg)(nil),
+		&MsgCompleteStorageProviderExit{},
+	)
+	registry.RegisterImplementations((*sdk.Msg)(nil),
+		&MsgSwapOut{},
+	)
+	registry.RegisterImplementations((*sdk.Msg)(nil),
+		&MsgDeposit{},
+	)
+	registry.RegisterImplementations((*sdk.Msg)(nil),
+		&MsgWithdraw{},
+	)
+	registry.RegisterImplementations((*sdk.Msg)(nil),
+		&MsgSettle{},
+	)
+	registry.RegisterImplementations((*sdk.Msg)(nil),
+		&MsgUpdateParams{},
+	)
+	registry.RegisterImplementations((*sdk.Msg)(nil),
+		&MsgCompleteSwapOut{},
+	)
+	registry.RegisterImplementations((*sdk.Msg)(nil),
+		&MsgCancelSwapOut{},
+	)
+	// this line is used by starport scaffolding # 3
+
+	msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
+}
+
+var (
+	Amino     = codec.NewLegacyAmino()
+	ModuleCdc = codec.NewProtoCodec(cdctypes.NewInterfaceRegistry())
+)
diff --git a/x/virtualgroup/types/errors.go b/x/virtualgroup/types/errors.go
new file mode 100644
index 000000000..a16c2ca9e
--- /dev/null
+++ b/x/virtualgroup/types/errors.go
@@ -0,0 +1,26 @@
+package types
+
+import (
+	"cosmossdk.io/errors"
+)
+
+// x/virtualgroup module sentinel errors
+var (
+	ErrGVGFamilyNotExist      = errors.Register(ModuleName, 1100, "global virtual group family not exist.")
+	ErrGVGNotExistInFamily    = errors.Register(ModuleName, 1101, "global virtual group not exist in family.")
+	ErrGVGNotExist            = errors.Register(ModuleName, 1102, "global virtual group not exist.")
+	ErrGVGNotEmpty            = errors.Register(ModuleName, 1103, "the store size of gvg is not zero")
+	ErrGenSequenceIDError     = errors.Register(ModuleName, 1104, "generate sequence id error.")
+	ErrWithdrawAmountTooLarge = errors.Register(ModuleName, 1105, "withdrawal amount is too large.")
+	ErrSwapOutFailed          = errors.Register(ModuleName, 1106, "swap out failed.")
+	ErrLVGNotExist            = errors.Register(ModuleName, 1107, "local virtual group not exist.")
+	ErrSPCanNotExit           = errors.Register(ModuleName, 1108, "the sp can not exit now.")
+	ErrSettleFailed           = errors.Register(ModuleName, 1109, "fail to settle.")
+	ErrInvalidGVGCount        = errors.Register(ModuleName, 1120, "the count of global virtual group ids is invalid.")
+	ErrWithdrawFailed         = errors.Register(ModuleName, 1121, "with draw failed.")
+
+	ErrLimitationExceed    = errors.Register(ModuleName, 1123, "limitation exceed.")
+	ErrInsufficientStaking = errors.Register(ModuleName, 1125, "insufficient staking for gvg")
+
+	ErrInvalidDenom = errors.Register(ModuleName, 2000, "Invalid denom.")
+)
diff --git a/x/virtualgroup/types/events.pb.go b/x/virtualgroup/types/events.pb.go
new file mode 100644
index 000000000..9d55414d1
--- /dev/null
+++ b/x/virtualgroup/types/events.pb.go
@@ -0,0 +1,3238 @@
+// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// source: greenfield/virtualgroup/events.proto
+
+package types
+
+import (
+	fmt "fmt"
+	_ "github.com/cosmos/cosmos-proto"
+	github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types"
+	_ "github.com/cosmos/gogoproto/gogoproto"
+	proto "github.com/cosmos/gogoproto/proto"
+	io "io"
+	math "math"
+	math_bits "math/bits"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
+
+type EventCreateGlobalVirtualGroup struct {
+	// The unique id of global virtual group, which is generated by blockchain
+	Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
+	// The id of the global virtual group family where the gvg belongs
+	FamilyId uint32 `protobuf:"varint,2,opt,name=family_id,json=familyId,proto3" json:"family_id,omitempty"`
+	// The id of the primary sp who create this global virtual group
+	PrimarySpId uint32 `protobuf:"varint,3,opt,name=primary_sp_id,json=primarySpId,proto3" json:"primary_sp_id,omitempty"`
+	// The ids of the secondary sps which belongs to this global virtual group
+	SecondarySpIds []uint32 `protobuf:"varint,4,rep,packed,name=secondary_sp_ids,json=secondarySpIds,proto3" json:"secondary_sp_ids,omitempty"`
+	// The store size of all the objects stores in this global virtual group
+	StoredSize uint64 `protobuf:"varint,5,opt,name=stored_size,json=storedSize,proto3" json:"stored_size,omitempty"`
+	// The virtual payment address of this global virtual group, which is auto generated by blockchain
+	// And, all users' payment flows will flow to this account
+	VirtualPaymentAddress string `protobuf:"bytes,6,opt,name=virtual_payment_address,json=virtualPaymentAddress,proto3" json:"virtual_payment_address,omitempty"`
+	// The total amount of the staking for this global virtual group
+	TotalDeposit github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,7,opt,name=total_deposit,json=totalDeposit,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"total_deposit"`
+}
+
+func (m *EventCreateGlobalVirtualGroup) Reset()         { *m = EventCreateGlobalVirtualGroup{} }
+func (m *EventCreateGlobalVirtualGroup) String() string { return proto.CompactTextString(m) }
+func (*EventCreateGlobalVirtualGroup) ProtoMessage()    {}
+func (*EventCreateGlobalVirtualGroup) Descriptor() ([]byte, []int) {
+	return fileDescriptor_ece39ea12016bd5b, []int{0}
+}
+func (m *EventCreateGlobalVirtualGroup) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *EventCreateGlobalVirtualGroup) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_EventCreateGlobalVirtualGroup.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *EventCreateGlobalVirtualGroup) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_EventCreateGlobalVirtualGroup.Merge(m, src)
+}
+func (m *EventCreateGlobalVirtualGroup) XXX_Size() int {
+	return m.Size()
+}
+func (m *EventCreateGlobalVirtualGroup) XXX_DiscardUnknown() {
+	xxx_messageInfo_EventCreateGlobalVirtualGroup.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_EventCreateGlobalVirtualGroup proto.InternalMessageInfo
+
+func (m *EventCreateGlobalVirtualGroup) GetId() uint32 {
+	if m != nil {
+		return m.Id
+	}
+	return 0
+}
+
+func (m *EventCreateGlobalVirtualGroup) GetFamilyId() uint32 {
+	if m != nil {
+		return m.FamilyId
+	}
+	return 0
+}
+
+func (m *EventCreateGlobalVirtualGroup) GetPrimarySpId() uint32 {
+	if m != nil {
+		return m.PrimarySpId
+	}
+	return 0
+}
+
+func (m *EventCreateGlobalVirtualGroup) GetSecondarySpIds() []uint32 {
+	if m != nil {
+		return m.SecondarySpIds
+	}
+	return nil
+}
+
+func (m *EventCreateGlobalVirtualGroup) GetStoredSize() uint64 {
+	if m != nil {
+		return m.StoredSize
+	}
+	return 0
+}
+
+func (m *EventCreateGlobalVirtualGroup) GetVirtualPaymentAddress() string {
+	if m != nil {
+		return m.VirtualPaymentAddress
+	}
+	return ""
+}
+
+type EventCreateGlobalVirtualGroupFamily struct {
+	// The id of global virtual group family, which is auto generated by blockchain
+	Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
+	// The virtual payment address of this global virtual group family, which is auto generated by blockcahin
+	// all users' read quota payment flows will flow to this account.
+	VirtualPaymentAddress string `protobuf:"bytes,2,opt,name=virtual_payment_address,json=virtualPaymentAddress,proto3" json:"virtual_payment_address,omitempty"`
+}
+
+func (m *EventCreateGlobalVirtualGroupFamily) Reset()         { *m = EventCreateGlobalVirtualGroupFamily{} }
+func (m *EventCreateGlobalVirtualGroupFamily) String() string { return proto.CompactTextString(m) }
+func (*EventCreateGlobalVirtualGroupFamily) ProtoMessage()    {}
+func (*EventCreateGlobalVirtualGroupFamily) Descriptor() ([]byte, []int) {
+	return fileDescriptor_ece39ea12016bd5b, []int{1}
+}
+func (m *EventCreateGlobalVirtualGroupFamily) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *EventCreateGlobalVirtualGroupFamily) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_EventCreateGlobalVirtualGroupFamily.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *EventCreateGlobalVirtualGroupFamily) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_EventCreateGlobalVirtualGroupFamily.Merge(m, src)
+}
+func (m *EventCreateGlobalVirtualGroupFamily) XXX_Size() int {
+	return m.Size()
+}
+func (m *EventCreateGlobalVirtualGroupFamily) XXX_DiscardUnknown() {
+	xxx_messageInfo_EventCreateGlobalVirtualGroupFamily.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_EventCreateGlobalVirtualGroupFamily proto.InternalMessageInfo
+
+func (m *EventCreateGlobalVirtualGroupFamily) GetId() uint32 {
+	if m != nil {
+		return m.Id
+	}
+	return 0
+}
+
+func (m *EventCreateGlobalVirtualGroupFamily) GetVirtualPaymentAddress() string {
+	if m != nil {
+		return m.VirtualPaymentAddress
+	}
+	return ""
+}
+
+type EventDeleteGlobalVirtualGroup struct {
+	// The id of global virtual group, which has been deleted
+	Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
+}
+
+func (m *EventDeleteGlobalVirtualGroup) Reset()         { *m = EventDeleteGlobalVirtualGroup{} }
+func (m *EventDeleteGlobalVirtualGroup) String() string { return proto.CompactTextString(m) }
+func (*EventDeleteGlobalVirtualGroup) ProtoMessage()    {}
+func (*EventDeleteGlobalVirtualGroup) Descriptor() ([]byte, []int) {
+	return fileDescriptor_ece39ea12016bd5b, []int{2}
+}
+func (m *EventDeleteGlobalVirtualGroup) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *EventDeleteGlobalVirtualGroup) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_EventDeleteGlobalVirtualGroup.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *EventDeleteGlobalVirtualGroup) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_EventDeleteGlobalVirtualGroup.Merge(m, src)
+}
+func (m *EventDeleteGlobalVirtualGroup) XXX_Size() int {
+	return m.Size()
+}
+func (m *EventDeleteGlobalVirtualGroup) XXX_DiscardUnknown() {
+	xxx_messageInfo_EventDeleteGlobalVirtualGroup.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_EventDeleteGlobalVirtualGroup proto.InternalMessageInfo
+
+func (m *EventDeleteGlobalVirtualGroup) GetId() uint32 {
+	if m != nil {
+		return m.Id
+	}
+	return 0
+}
+
+type EventUpdateGlobalVirtualGroup struct {
+	// The id of global virtual group, which has been updated
+	Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
+	// The store size of all the objects stores in this global virtual group
+	StoreSize uint64 `protobuf:"varint,2,opt,name=store_size,json=storeSize,proto3" json:"store_size,omitempty"`
+	// The total amount of the staking for this global virtual group
+	TotalDeposit github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,3,opt,name=total_deposit,json=totalDeposit,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"total_deposit"`
+}
+
+func (m *EventUpdateGlobalVirtualGroup) Reset()         { *m = EventUpdateGlobalVirtualGroup{} }
+func (m *EventUpdateGlobalVirtualGroup) String() string { return proto.CompactTextString(m) }
+func (*EventUpdateGlobalVirtualGroup) ProtoMessage()    {}
+func (*EventUpdateGlobalVirtualGroup) Descriptor() ([]byte, []int) {
+	return fileDescriptor_ece39ea12016bd5b, []int{3}
+}
+func (m *EventUpdateGlobalVirtualGroup) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *EventUpdateGlobalVirtualGroup) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_EventUpdateGlobalVirtualGroup.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *EventUpdateGlobalVirtualGroup) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_EventUpdateGlobalVirtualGroup.Merge(m, src)
+}
+func (m *EventUpdateGlobalVirtualGroup) XXX_Size() int {
+	return m.Size()
+}
+func (m *EventUpdateGlobalVirtualGroup) XXX_DiscardUnknown() {
+	xxx_messageInfo_EventUpdateGlobalVirtualGroup.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_EventUpdateGlobalVirtualGroup proto.InternalMessageInfo
+
+func (m *EventUpdateGlobalVirtualGroup) GetId() uint32 {
+	if m != nil {
+		return m.Id
+	}
+	return 0
+}
+
+func (m *EventUpdateGlobalVirtualGroup) GetStoreSize() uint64 {
+	if m != nil {
+		return m.StoreSize
+	}
+	return 0
+}
+
+type EventCreateLocalVirtualGroup struct {
+	// The id of the local virtual group and this ID is unique within the bucket
+	// and different buckets will have the same ID
+	Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
+	// The id of the bucket
+	BucketId Uint `protobuf:"bytes,2,opt,name=bucket_id,json=bucketId,proto3,customtype=Uint" json:"bucket_id"`
+	// The id of the global virtual group
+	GlobalVirtualGroupId uint32 `protobuf:"varint,3,opt,name=global_virtual_group_id,json=globalVirtualGroupId,proto3" json:"global_virtual_group_id,omitempty"`
+	// The stored size of all the objects stores in this lvg
+	StoredSize uint64 `protobuf:"varint,4,opt,name=stored_size,json=storedSize,proto3" json:"stored_size,omitempty"`
+}
+
+func (m *EventCreateLocalVirtualGroup) Reset()         { *m = EventCreateLocalVirtualGroup{} }
+func (m *EventCreateLocalVirtualGroup) String() string { return proto.CompactTextString(m) }
+func (*EventCreateLocalVirtualGroup) ProtoMessage()    {}
+func (*EventCreateLocalVirtualGroup) Descriptor() ([]byte, []int) {
+	return fileDescriptor_ece39ea12016bd5b, []int{4}
+}
+func (m *EventCreateLocalVirtualGroup) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *EventCreateLocalVirtualGroup) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_EventCreateLocalVirtualGroup.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *EventCreateLocalVirtualGroup) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_EventCreateLocalVirtualGroup.Merge(m, src)
+}
+func (m *EventCreateLocalVirtualGroup) XXX_Size() int {
+	return m.Size()
+}
+func (m *EventCreateLocalVirtualGroup) XXX_DiscardUnknown() {
+	xxx_messageInfo_EventCreateLocalVirtualGroup.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_EventCreateLocalVirtualGroup proto.InternalMessageInfo
+
+func (m *EventCreateLocalVirtualGroup) GetId() uint32 {
+	if m != nil {
+		return m.Id
+	}
+	return 0
+}
+
+func (m *EventCreateLocalVirtualGroup) GetGlobalVirtualGroupId() uint32 {
+	if m != nil {
+		return m.GlobalVirtualGroupId
+	}
+	return 0
+}
+
+func (m *EventCreateLocalVirtualGroup) GetStoredSize() uint64 {
+	if m != nil {
+		return m.StoredSize
+	}
+	return 0
+}
+
+type EventUpdateLocalVirtualGroup struct {
+	// The id of the local virtual group
+	Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
+	// The id of the global virtual group
+	GlobalVirtualGroupId uint32 `protobuf:"varint,2,opt,name=global_virtual_group_id,json=globalVirtualGroupId,proto3" json:"global_virtual_group_id,omitempty"`
+	// The stored size of all the objects stores in this lvg
+	StoredSize uint64 `protobuf:"varint,3,opt,name=stored_size,json=storedSize,proto3" json:"stored_size,omitempty"`
+}
+
+func (m *EventUpdateLocalVirtualGroup) Reset()         { *m = EventUpdateLocalVirtualGroup{} }
+func (m *EventUpdateLocalVirtualGroup) String() string { return proto.CompactTextString(m) }
+func (*EventUpdateLocalVirtualGroup) ProtoMessage()    {}
+func (*EventUpdateLocalVirtualGroup) Descriptor() ([]byte, []int) {
+	return fileDescriptor_ece39ea12016bd5b, []int{5}
+}
+func (m *EventUpdateLocalVirtualGroup) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *EventUpdateLocalVirtualGroup) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_EventUpdateLocalVirtualGroup.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *EventUpdateLocalVirtualGroup) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_EventUpdateLocalVirtualGroup.Merge(m, src)
+}
+func (m *EventUpdateLocalVirtualGroup) XXX_Size() int {
+	return m.Size()
+}
+func (m *EventUpdateLocalVirtualGroup) XXX_DiscardUnknown() {
+	xxx_messageInfo_EventUpdateLocalVirtualGroup.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_EventUpdateLocalVirtualGroup proto.InternalMessageInfo
+
+func (m *EventUpdateLocalVirtualGroup) GetId() uint32 {
+	if m != nil {
+		return m.Id
+	}
+	return 0
+}
+
+func (m *EventUpdateLocalVirtualGroup) GetGlobalVirtualGroupId() uint32 {
+	if m != nil {
+		return m.GlobalVirtualGroupId
+	}
+	return 0
+}
+
+func (m *EventUpdateLocalVirtualGroup) GetStoredSize() uint64 {
+	if m != nil {
+		return m.StoredSize
+	}
+	return 0
+}
+
+type EventSwapOut struct {
+	// The id of the storage provider who wants to swap out
+	StorageProviderId uint32 `protobuf:"varint,1,opt,name=storage_provider_id,json=storageProviderId,proto3" json:"storage_provider_id,omitempty"`
+	// The id of the gvg family which the storage provider wants to swap out as primary sp
+	GlobalVirtualGroupFamilyId uint32 `protobuf:"varint,2,opt,name=global_virtual_group_family_id,json=globalVirtualGroupFamilyId,proto3" json:"global_virtual_group_family_id,omitempty"`
+	// The ids of the gvgs which the storage provider wants to swap out as secondary sp
+	GlobalVirtualGroupIds []uint32 `protobuf:"varint,3,rep,packed,name=global_virtual_group_ids,json=globalVirtualGroupIds,proto3" json:"global_virtual_group_ids,omitempty"`
+	// The id of the successor sp who take over this family or these gvgs
+	SuccessorSpId uint32 `protobuf:"varint,4,opt,name=successor_sp_id,json=successorSpId,proto3" json:"successor_sp_id,omitempty"`
+}
+
+func (m *EventSwapOut) Reset()         { *m = EventSwapOut{} }
+func (m *EventSwapOut) String() string { return proto.CompactTextString(m) }
+func (*EventSwapOut) ProtoMessage()    {}
+func (*EventSwapOut) Descriptor() ([]byte, []int) {
+	return fileDescriptor_ece39ea12016bd5b, []int{6}
+}
+func (m *EventSwapOut) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *EventSwapOut) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_EventSwapOut.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *EventSwapOut) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_EventSwapOut.Merge(m, src)
+}
+func (m *EventSwapOut) XXX_Size() int {
+	return m.Size()
+}
+func (m *EventSwapOut) XXX_DiscardUnknown() {
+	xxx_messageInfo_EventSwapOut.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_EventSwapOut proto.InternalMessageInfo
+
+func (m *EventSwapOut) GetStorageProviderId() uint32 {
+	if m != nil {
+		return m.StorageProviderId
+	}
+	return 0
+}
+
+func (m *EventSwapOut) GetGlobalVirtualGroupFamilyId() uint32 {
+	if m != nil {
+		return m.GlobalVirtualGroupFamilyId
+	}
+	return 0
+}
+
+func (m *EventSwapOut) GetGlobalVirtualGroupIds() []uint32 {
+	if m != nil {
+		return m.GlobalVirtualGroupIds
+	}
+	return nil
+}
+
+func (m *EventSwapOut) GetSuccessorSpId() uint32 {
+	if m != nil {
+		return m.SuccessorSpId
+	}
+	return 0
+}
+
+type EventCompleteSwapOut struct {
+	// The id of the storage provider who complete swap out.
+	StorageProviderId uint32 `protobuf:"varint,1,opt,name=storage_provider_id,json=storageProviderId,proto3" json:"storage_provider_id,omitempty"`
+	// The id of the storage provider who swap out the family or gvgs
+	SrcStorageProviderId uint32 `protobuf:"varint,2,opt,name=src_storage_provider_id,json=srcStorageProviderId,proto3" json:"src_storage_provider_id,omitempty"`
+	// The id of the gvg family
+	GlobalVirtualGroupFamilyId uint32 `protobuf:"varint,3,opt,name=global_virtual_group_family_id,json=globalVirtualGroupFamilyId,proto3" json:"global_virtual_group_family_id,omitempty"`
+	// The ids of the gvgs
+	GlobalVirtualGroupIds []uint32 `protobuf:"varint,4,rep,packed,name=global_virtual_group_ids,json=globalVirtualGroupIds,proto3" json:"global_virtual_group_ids,omitempty"`
+}
+
+func (m *EventCompleteSwapOut) Reset()         { *m = EventCompleteSwapOut{} }
+func (m *EventCompleteSwapOut) String() string { return proto.CompactTextString(m) }
+func (*EventCompleteSwapOut) ProtoMessage()    {}
+func (*EventCompleteSwapOut) Descriptor() ([]byte, []int) {
+	return fileDescriptor_ece39ea12016bd5b, []int{7}
+}
+func (m *EventCompleteSwapOut) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *EventCompleteSwapOut) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_EventCompleteSwapOut.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *EventCompleteSwapOut) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_EventCompleteSwapOut.Merge(m, src)
+}
+func (m *EventCompleteSwapOut) XXX_Size() int {
+	return m.Size()
+}
+func (m *EventCompleteSwapOut) XXX_DiscardUnknown() {
+	xxx_messageInfo_EventCompleteSwapOut.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_EventCompleteSwapOut proto.InternalMessageInfo
+
+func (m *EventCompleteSwapOut) GetStorageProviderId() uint32 {
+	if m != nil {
+		return m.StorageProviderId
+	}
+	return 0
+}
+
+func (m *EventCompleteSwapOut) GetSrcStorageProviderId() uint32 {
+	if m != nil {
+		return m.SrcStorageProviderId
+	}
+	return 0
+}
+
+func (m *EventCompleteSwapOut) GetGlobalVirtualGroupFamilyId() uint32 {
+	if m != nil {
+		return m.GlobalVirtualGroupFamilyId
+	}
+	return 0
+}
+
+func (m *EventCompleteSwapOut) GetGlobalVirtualGroupIds() []uint32 {
+	if m != nil {
+		return m.GlobalVirtualGroupIds
+	}
+	return nil
+}
+
+type EventCancelSwapOut struct {
+	// The id of the storage provider who cancel swap out.
+	StorageProviderId uint32 `protobuf:"varint,1,opt,name=storage_provider_id,json=storageProviderId,proto3" json:"storage_provider_id,omitempty"`
+	// The id of the gvg family
+	GlobalVirtualGroupFamilyId uint32 `protobuf:"varint,2,opt,name=global_virtual_group_family_id,json=globalVirtualGroupFamilyId,proto3" json:"global_virtual_group_family_id,omitempty"`
+	// The ids of the gvgs
+	GlobalVirtualGroupIds []uint32 `protobuf:"varint,3,rep,packed,name=global_virtual_group_ids,json=globalVirtualGroupIds,proto3" json:"global_virtual_group_ids,omitempty"`
+	// The id of the successor sp who take over this family or these gvgs
+	SuccessorSpId uint32 `protobuf:"varint,4,opt,name=successor_sp_id,json=successorSpId,proto3" json:"successor_sp_id,omitempty"`
+}
+
+func (m *EventCancelSwapOut) Reset()         { *m = EventCancelSwapOut{} }
+func (m *EventCancelSwapOut) String() string { return proto.CompactTextString(m) }
+func (*EventCancelSwapOut) ProtoMessage()    {}
+func (*EventCancelSwapOut) Descriptor() ([]byte, []int) {
+	return fileDescriptor_ece39ea12016bd5b, []int{8}
+}
+func (m *EventCancelSwapOut) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *EventCancelSwapOut) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_EventCancelSwapOut.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *EventCancelSwapOut) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_EventCancelSwapOut.Merge(m, src)
+}
+func (m *EventCancelSwapOut) XXX_Size() int {
+	return m.Size()
+}
+func (m *EventCancelSwapOut) XXX_DiscardUnknown() {
+	xxx_messageInfo_EventCancelSwapOut.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_EventCancelSwapOut proto.InternalMessageInfo
+
+func (m *EventCancelSwapOut) GetStorageProviderId() uint32 {
+	if m != nil {
+		return m.StorageProviderId
+	}
+	return 0
+}
+
+func (m *EventCancelSwapOut) GetGlobalVirtualGroupFamilyId() uint32 {
+	if m != nil {
+		return m.GlobalVirtualGroupFamilyId
+	}
+	return 0
+}
+
+func (m *EventCancelSwapOut) GetGlobalVirtualGroupIds() []uint32 {
+	if m != nil {
+		return m.GlobalVirtualGroupIds
+	}
+	return nil
+}
+
+func (m *EventCancelSwapOut) GetSuccessorSpId() uint32 {
+	if m != nil {
+		return m.SuccessorSpId
+	}
+	return 0
+}
+
+type EventStorageProviderExit struct {
+	// The id of the storage provider who wants to exit
+	StorageProviderId uint32 `protobuf:"varint,1,opt,name=storage_provider_id,json=storageProviderId,proto3" json:"storage_provider_id,omitempty"`
+	// The operator address of the storage provider who wants to exit
+	OperatorAddress string `protobuf:"bytes,2,opt,name=operator_address,json=operatorAddress,proto3" json:"operator_address,omitempty"`
+}
+
+func (m *EventStorageProviderExit) Reset()         { *m = EventStorageProviderExit{} }
+func (m *EventStorageProviderExit) String() string { return proto.CompactTextString(m) }
+func (*EventStorageProviderExit) ProtoMessage()    {}
+func (*EventStorageProviderExit) Descriptor() ([]byte, []int) {
+	return fileDescriptor_ece39ea12016bd5b, []int{9}
+}
+func (m *EventStorageProviderExit) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *EventStorageProviderExit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_EventStorageProviderExit.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *EventStorageProviderExit) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_EventStorageProviderExit.Merge(m, src)
+}
+func (m *EventStorageProviderExit) XXX_Size() int {
+	return m.Size()
+}
+func (m *EventStorageProviderExit) XXX_DiscardUnknown() {
+	xxx_messageInfo_EventStorageProviderExit.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_EventStorageProviderExit proto.InternalMessageInfo
+
+func (m *EventStorageProviderExit) GetStorageProviderId() uint32 {
+	if m != nil {
+		return m.StorageProviderId
+	}
+	return 0
+}
+
+func (m *EventStorageProviderExit) GetOperatorAddress() string {
+	if m != nil {
+		return m.OperatorAddress
+	}
+	return ""
+}
+
+type EventCompleteStorageProviderExit struct {
+	// The id of the storage provider who complete exit
+	StorageProviderId uint32 `protobuf:"varint,1,opt,name=storage_provider_id,json=storageProviderId,proto3" json:"storage_provider_id,omitempty"`
+	// The operator address which initial the complete exit transaction
+	OperatorAddress string `protobuf:"bytes,2,opt,name=operator_address,json=operatorAddress,proto3" json:"operator_address,omitempty"`
+	// total_deposit defines the number of tokens deposited by this storage provider for staking.
+	TotalDeposit github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,3,opt,name=total_deposit,json=totalDeposit,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"total_deposit"`
+}
+
+func (m *EventCompleteStorageProviderExit) Reset()         { *m = EventCompleteStorageProviderExit{} }
+func (m *EventCompleteStorageProviderExit) String() string { return proto.CompactTextString(m) }
+func (*EventCompleteStorageProviderExit) ProtoMessage()    {}
+func (*EventCompleteStorageProviderExit) Descriptor() ([]byte, []int) {
+	return fileDescriptor_ece39ea12016bd5b, []int{10}
+}
+func (m *EventCompleteStorageProviderExit) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *EventCompleteStorageProviderExit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_EventCompleteStorageProviderExit.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *EventCompleteStorageProviderExit) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_EventCompleteStorageProviderExit.Merge(m, src)
+}
+func (m *EventCompleteStorageProviderExit) XXX_Size() int {
+	return m.Size()
+}
+func (m *EventCompleteStorageProviderExit) XXX_DiscardUnknown() {
+	xxx_messageInfo_EventCompleteStorageProviderExit.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_EventCompleteStorageProviderExit proto.InternalMessageInfo
+
+func (m *EventCompleteStorageProviderExit) GetStorageProviderId() uint32 {
+	if m != nil {
+		return m.StorageProviderId
+	}
+	return 0
+}
+
+func (m *EventCompleteStorageProviderExit) GetOperatorAddress() string {
+	if m != nil {
+		return m.OperatorAddress
+	}
+	return ""
+}
+
+func init() {
+	proto.RegisterType((*EventCreateGlobalVirtualGroup)(nil), "greenfield.virtualgroup.EventCreateGlobalVirtualGroup")
+	proto.RegisterType((*EventCreateGlobalVirtualGroupFamily)(nil), "greenfield.virtualgroup.EventCreateGlobalVirtualGroupFamily")
+	proto.RegisterType((*EventDeleteGlobalVirtualGroup)(nil), "greenfield.virtualgroup.EventDeleteGlobalVirtualGroup")
+	proto.RegisterType((*EventUpdateGlobalVirtualGroup)(nil), "greenfield.virtualgroup.EventUpdateGlobalVirtualGroup")
+	proto.RegisterType((*EventCreateLocalVirtualGroup)(nil), "greenfield.virtualgroup.EventCreateLocalVirtualGroup")
+	proto.RegisterType((*EventUpdateLocalVirtualGroup)(nil), "greenfield.virtualgroup.EventUpdateLocalVirtualGroup")
+	proto.RegisterType((*EventSwapOut)(nil), "greenfield.virtualgroup.EventSwapOut")
+	proto.RegisterType((*EventCompleteSwapOut)(nil), "greenfield.virtualgroup.EventCompleteSwapOut")
+	proto.RegisterType((*EventCancelSwapOut)(nil), "greenfield.virtualgroup.EventCancelSwapOut")
+	proto.RegisterType((*EventStorageProviderExit)(nil), "greenfield.virtualgroup.EventStorageProviderExit")
+	proto.RegisterType((*EventCompleteStorageProviderExit)(nil), "greenfield.virtualgroup.EventCompleteStorageProviderExit")
+}
+
+func init() {
+	proto.RegisterFile("greenfield/virtualgroup/events.proto", fileDescriptor_ece39ea12016bd5b)
+}
+
+var fileDescriptor_ece39ea12016bd5b = []byte{
+	// 747 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x56, 0x3f, 0x4f, 0x1b, 0x49,
+	0x14, 0xf7, 0xd8, 0x3e, 0x0e, 0x0f, 0x18, 0xb8, 0x3d, 0x23, 0xef, 0xc1, 0x61, 0xac, 0xbd, 0x13,
+	0x72, 0x63, 0xbb, 0xb8, 0x43, 0x77, 0xc5, 0x35, 0x67, 0x20, 0x68, 0xa5, 0x28, 0x41, 0xb6, 0x48,
+	0x91, 0x66, 0xb5, 0xde, 0x19, 0x96, 0x11, 0xeb, 0x9d, 0xd5, 0xcc, 0x98, 0x60, 0x3e, 0x40, 0x52,
+	0x26, 0x5f, 0x25, 0x12, 0x1f, 0x20, 0x25, 0x25, 0xa2, 0x8a, 0x52, 0xa0, 0x08, 0x57, 0xe9, 0x52,
+	0x53, 0x45, 0x3b, 0x33, 0x36, 0x06, 0x83, 0x71, 0x48, 0x14, 0x45, 0xa9, 0xec, 0x7d, 0xf3, 0xfe,
+	0xfc, 0x7e, 0xbf, 0xf7, 0xe6, 0x69, 0xe0, 0x9f, 0x3e, 0xc3, 0x38, 0xdc, 0x21, 0x38, 0x40, 0xd5,
+	0x7d, 0xc2, 0x44, 0xdb, 0x0d, 0x7c, 0x46, 0xdb, 0x51, 0x15, 0xef, 0xe3, 0x50, 0xf0, 0x4a, 0xc4,
+	0xa8, 0xa0, 0x46, 0xfe, 0xd2, 0xab, 0x32, 0xe8, 0xb5, 0xf0, 0x9b, 0x47, 0x79, 0x8b, 0x72, 0x47,
+	0xba, 0x55, 0xd5, 0x87, 0x8a, 0x59, 0xc8, 0xf9, 0xd4, 0xa7, 0xca, 0x1e, 0xff, 0x53, 0x56, 0xeb,
+	0x63, 0x12, 0x2e, 0x6d, 0xc4, 0xa9, 0xd7, 0x18, 0x76, 0x05, 0xde, 0x0c, 0x68, 0xd3, 0x0d, 0x9e,
+	0xa8, 0x94, 0x9b, 0x71, 0x4a, 0x63, 0x06, 0x26, 0x09, 0x32, 0x41, 0x11, 0x94, 0xb2, 0xf5, 0x24,
+	0x41, 0xc6, 0x22, 0xcc, 0xec, 0xb8, 0x2d, 0x12, 0x74, 0x1c, 0x82, 0xcc, 0xa4, 0x34, 0x4f, 0x2a,
+	0x83, 0x8d, 0x0c, 0x0b, 0x66, 0x23, 0x46, 0x5a, 0x2e, 0xeb, 0x38, 0x3c, 0x8a, 0x1d, 0x52, 0xd2,
+	0x61, 0x4a, 0x1b, 0x1b, 0x91, 0x8d, 0x8c, 0x12, 0x9c, 0xe3, 0xd8, 0xa3, 0x21, 0xea, 0x7b, 0x71,
+	0x33, 0x5d, 0x4c, 0x95, 0xb2, 0xf5, 0x99, 0xbe, 0x3d, 0x76, 0xe4, 0xc6, 0x32, 0x9c, 0xe2, 0x82,
+	0x32, 0x8c, 0x1c, 0x4e, 0x0e, 0xb1, 0xf9, 0x53, 0x11, 0x94, 0xd2, 0x75, 0xa8, 0x4c, 0x0d, 0x72,
+	0x88, 0x8d, 0x2d, 0x98, 0xd7, 0xf4, 0x9d, 0xc8, 0xed, 0xb4, 0x70, 0x28, 0x1c, 0x17, 0x21, 0x86,
+	0x39, 0x37, 0x27, 0x8a, 0xa0, 0x94, 0xa9, 0x99, 0xa7, 0x47, 0xe5, 0x9c, 0x96, 0xe1, 0x7f, 0x75,
+	0xd2, 0x10, 0x8c, 0x84, 0x7e, 0x7d, 0x5e, 0x07, 0x6e, 0xa9, 0x38, 0x7d, 0x68, 0xb8, 0x30, 0x2b,
+	0xa8, 0x70, 0x03, 0x07, 0xe1, 0x88, 0x72, 0x22, 0xcc, 0x9f, 0x65, 0x9e, 0xff, 0x8e, 0xcf, 0x96,
+	0x13, 0xef, 0xce, 0x96, 0x57, 0x7c, 0x22, 0x76, 0xdb, 0xcd, 0x8a, 0x47, 0x5b, 0x5a, 0x5d, 0xfd,
+	0x53, 0xe6, 0x68, 0xaf, 0x2a, 0x3a, 0x11, 0xe6, 0x15, 0x3b, 0x14, 0xa7, 0x47, 0x65, 0xa8, 0xab,
+	0xda, 0xa1, 0xa8, 0x4f, 0xcb, 0x94, 0xeb, 0x2a, 0xa3, 0xf5, 0x02, 0xc0, 0x3f, 0x46, 0x4a, 0xfe,
+	0x40, 0xaa, 0x39, 0x24, 0xfc, 0x08, 0xb2, 0xc9, 0x7b, 0x91, 0xb5, 0xaa, 0xba, 0xf7, 0xeb, 0x38,
+	0xc0, 0xe3, 0xf4, 0xde, 0x7a, 0x0d, 0x74, 0xc4, 0x76, 0x84, 0xc6, 0x9b, 0x96, 0x25, 0xa8, 0xfa,
+	0xa5, 0x3a, 0x98, 0x94, 0x1d, 0xcc, 0x48, 0x8b, 0x6c, 0xe0, 0x90, 0xdc, 0xa9, 0xaf, 0x2e, 0xf7,
+	0x1b, 0x00, 0x7f, 0x1f, 0x90, 0xfb, 0x21, 0xf5, 0xee, 0x80, 0xfc, 0x2f, 0xcc, 0x34, 0xdb, 0xde,
+	0x1e, 0x16, 0xbd, 0x01, 0xcf, 0xd4, 0x16, 0x35, 0x9e, 0xf4, 0x36, 0x91, 0xd5, 0xa6, 0x74, 0xb5,
+	0xf8, 0xb3, 0x3e, 0xa9, 0xbc, 0x6d, 0x64, 0xac, 0xc2, 0xbc, 0x2f, 0x25, 0x71, 0x7a, 0x8d, 0x92,
+	0xb7, 0xf2, 0xf2, 0x1e, 0xe4, 0xfc, 0x21, 0xc5, 0x6c, 0x74, 0x7d, 0xcc, 0xd3, 0xd7, 0xc7, 0xdc,
+	0x7a, 0xde, 0xa3, 0xa0, 0x64, 0xbf, 0x9b, 0xc2, 0x08, 0x20, 0xc9, 0xf1, 0x81, 0xa4, 0x86, 0x80,
+	0x74, 0x01, 0x9c, 0x96, 0x40, 0x1a, 0xcf, 0xdc, 0xe8, 0x71, 0x5b, 0x18, 0x15, 0xf8, 0x6b, 0x7c,
+	0xec, 0xfa, 0x38, 0x5e, 0x39, 0xfb, 0x04, 0x61, 0xe6, 0xf4, 0x91, 0xfc, 0xa2, 0x8f, 0xb6, 0xf4,
+	0x89, 0x8d, 0x8c, 0x1a, 0x2c, 0xdc, 0x08, 0xec, 0xfa, 0x46, 0x59, 0xf0, 0x6f, 0xb9, 0x15, 0x36,
+	0x32, 0xfe, 0x81, 0xe6, 0x2d, 0xe4, 0xb8, 0x99, 0x92, 0x7b, 0x64, 0xfe, 0x26, 0x76, 0xdc, 0x58,
+	0x81, 0xb3, 0xbc, 0xed, 0x79, 0x98, 0x73, 0xca, 0xf4, 0x7a, 0x4a, 0xcb, 0x6a, 0xd9, 0xbe, 0x39,
+	0xde, 0x3b, 0xd6, 0x05, 0x80, 0x39, 0x35, 0x31, 0xb4, 0x15, 0xc5, 0x37, 0xe3, 0xbe, 0x6c, 0x57,
+	0x61, 0x9e, 0x33, 0xcf, 0xb9, 0x29, 0x46, 0xb7, 0x81, 0x33, 0xaf, 0x71, 0x0f, 0x91, 0x52, 0x5f,
+	0x24, 0x52, 0x7a, 0x84, 0x48, 0xd6, 0x07, 0x00, 0x0d, 0x45, 0xde, 0x0d, 0x3d, 0x1c, 0xfc, 0xd0,
+	0x8d, 0x7e, 0x09, 0xa0, 0xa9, 0xc6, 0xf9, 0x2a, 0xfe, 0x8d, 0x03, 0xf2, 0xf9, 0x8c, 0xd7, 0xe0,
+	0x1c, 0x8d, 0x30, 0x73, 0x05, 0x65, 0x63, 0xef, 0xe5, 0xd9, 0x5e, 0x44, 0x6f, 0x23, 0x5f, 0x00,
+	0x58, 0xbc, 0x3a, 0x7a, 0xdf, 0x09, 0xb2, 0x6f, 0xb0, 0xa9, 0x6b, 0x8f, 0x8e, 0xcf, 0x0b, 0xe0,
+	0xe4, 0xbc, 0x00, 0xde, 0x9f, 0x17, 0xc0, 0xab, 0x6e, 0x21, 0x71, 0xd2, 0x2d, 0x24, 0xde, 0x76,
+	0x0b, 0x89, 0xa7, 0x7f, 0x0f, 0x64, 0x6f, 0x86, 0xcd, 0xb2, 0xb7, 0xeb, 0x92, 0xb0, 0x3a, 0xf0,
+	0x54, 0x3a, 0xb8, 0xfa, 0x58, 0x92, 0xf5, 0x9a, 0x13, 0xf2, 0x89, 0xf3, 0xd7, 0xa7, 0x00, 0x00,
+	0x00, 0xff, 0xff, 0x89, 0x1e, 0x45, 0x35, 0x54, 0x09, 0x00, 0x00,
+}
+
+func (m *EventCreateGlobalVirtualGroup) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *EventCreateGlobalVirtualGroup) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *EventCreateGlobalVirtualGroup) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	{
+		size := m.TotalDeposit.Size()
+		i -= size
+		if _, err := m.TotalDeposit.MarshalTo(dAtA[i:]); err != nil {
+			return 0, err
+		}
+		i = encodeVarintEvents(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0x3a
+	if len(m.VirtualPaymentAddress) > 0 {
+		i -= len(m.VirtualPaymentAddress)
+		copy(dAtA[i:], m.VirtualPaymentAddress)
+		i = encodeVarintEvents(dAtA, i, uint64(len(m.VirtualPaymentAddress)))
+		i--
+		dAtA[i] = 0x32
+	}
+	if m.StoredSize != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.StoredSize))
+		i--
+		dAtA[i] = 0x28
+	}
+	if len(m.SecondarySpIds) > 0 {
+		dAtA2 := make([]byte, len(m.SecondarySpIds)*10)
+		var j1 int
+		for _, num := range m.SecondarySpIds {
+			for num >= 1<<7 {
+				dAtA2[j1] = uint8(uint64(num)&0x7f | 0x80)
+				num >>= 7
+				j1++
+			}
+			dAtA2[j1] = uint8(num)
+			j1++
+		}
+		i -= j1
+		copy(dAtA[i:], dAtA2[:j1])
+		i = encodeVarintEvents(dAtA, i, uint64(j1))
+		i--
+		dAtA[i] = 0x22
+	}
+	if m.PrimarySpId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.PrimarySpId))
+		i--
+		dAtA[i] = 0x18
+	}
+	if m.FamilyId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.FamilyId))
+		i--
+		dAtA[i] = 0x10
+	}
+	if m.Id != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.Id))
+		i--
+		dAtA[i] = 0x8
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *EventCreateGlobalVirtualGroupFamily) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *EventCreateGlobalVirtualGroupFamily) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *EventCreateGlobalVirtualGroupFamily) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.VirtualPaymentAddress) > 0 {
+		i -= len(m.VirtualPaymentAddress)
+		copy(dAtA[i:], m.VirtualPaymentAddress)
+		i = encodeVarintEvents(dAtA, i, uint64(len(m.VirtualPaymentAddress)))
+		i--
+		dAtA[i] = 0x12
+	}
+	if m.Id != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.Id))
+		i--
+		dAtA[i] = 0x8
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *EventDeleteGlobalVirtualGroup) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *EventDeleteGlobalVirtualGroup) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *EventDeleteGlobalVirtualGroup) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.Id != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.Id))
+		i--
+		dAtA[i] = 0x8
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *EventUpdateGlobalVirtualGroup) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *EventUpdateGlobalVirtualGroup) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *EventUpdateGlobalVirtualGroup) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	{
+		size := m.TotalDeposit.Size()
+		i -= size
+		if _, err := m.TotalDeposit.MarshalTo(dAtA[i:]); err != nil {
+			return 0, err
+		}
+		i = encodeVarintEvents(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0x1a
+	if m.StoreSize != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.StoreSize))
+		i--
+		dAtA[i] = 0x10
+	}
+	if m.Id != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.Id))
+		i--
+		dAtA[i] = 0x8
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *EventCreateLocalVirtualGroup) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *EventCreateLocalVirtualGroup) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *EventCreateLocalVirtualGroup) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.StoredSize != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.StoredSize))
+		i--
+		dAtA[i] = 0x20
+	}
+	if m.GlobalVirtualGroupId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.GlobalVirtualGroupId))
+		i--
+		dAtA[i] = 0x18
+	}
+	{
+		size := m.BucketId.Size()
+		i -= size
+		if _, err := m.BucketId.MarshalTo(dAtA[i:]); err != nil {
+			return 0, err
+		}
+		i = encodeVarintEvents(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0x12
+	if m.Id != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.Id))
+		i--
+		dAtA[i] = 0x8
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *EventUpdateLocalVirtualGroup) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *EventUpdateLocalVirtualGroup) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *EventUpdateLocalVirtualGroup) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.StoredSize != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.StoredSize))
+		i--
+		dAtA[i] = 0x18
+	}
+	if m.GlobalVirtualGroupId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.GlobalVirtualGroupId))
+		i--
+		dAtA[i] = 0x10
+	}
+	if m.Id != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.Id))
+		i--
+		dAtA[i] = 0x8
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *EventSwapOut) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *EventSwapOut) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *EventSwapOut) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.SuccessorSpId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.SuccessorSpId))
+		i--
+		dAtA[i] = 0x20
+	}
+	if len(m.GlobalVirtualGroupIds) > 0 {
+		dAtA4 := make([]byte, len(m.GlobalVirtualGroupIds)*10)
+		var j3 int
+		for _, num := range m.GlobalVirtualGroupIds {
+			for num >= 1<<7 {
+				dAtA4[j3] = uint8(uint64(num)&0x7f | 0x80)
+				num >>= 7
+				j3++
+			}
+			dAtA4[j3] = uint8(num)
+			j3++
+		}
+		i -= j3
+		copy(dAtA[i:], dAtA4[:j3])
+		i = encodeVarintEvents(dAtA, i, uint64(j3))
+		i--
+		dAtA[i] = 0x1a
+	}
+	if m.GlobalVirtualGroupFamilyId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.GlobalVirtualGroupFamilyId))
+		i--
+		dAtA[i] = 0x10
+	}
+	if m.StorageProviderId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.StorageProviderId))
+		i--
+		dAtA[i] = 0x8
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *EventCompleteSwapOut) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *EventCompleteSwapOut) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *EventCompleteSwapOut) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.GlobalVirtualGroupIds) > 0 {
+		dAtA6 := make([]byte, len(m.GlobalVirtualGroupIds)*10)
+		var j5 int
+		for _, num := range m.GlobalVirtualGroupIds {
+			for num >= 1<<7 {
+				dAtA6[j5] = uint8(uint64(num)&0x7f | 0x80)
+				num >>= 7
+				j5++
+			}
+			dAtA6[j5] = uint8(num)
+			j5++
+		}
+		i -= j5
+		copy(dAtA[i:], dAtA6[:j5])
+		i = encodeVarintEvents(dAtA, i, uint64(j5))
+		i--
+		dAtA[i] = 0x22
+	}
+	if m.GlobalVirtualGroupFamilyId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.GlobalVirtualGroupFamilyId))
+		i--
+		dAtA[i] = 0x18
+	}
+	if m.SrcStorageProviderId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.SrcStorageProviderId))
+		i--
+		dAtA[i] = 0x10
+	}
+	if m.StorageProviderId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.StorageProviderId))
+		i--
+		dAtA[i] = 0x8
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *EventCancelSwapOut) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *EventCancelSwapOut) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *EventCancelSwapOut) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.SuccessorSpId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.SuccessorSpId))
+		i--
+		dAtA[i] = 0x20
+	}
+	if len(m.GlobalVirtualGroupIds) > 0 {
+		dAtA8 := make([]byte, len(m.GlobalVirtualGroupIds)*10)
+		var j7 int
+		for _, num := range m.GlobalVirtualGroupIds {
+			for num >= 1<<7 {
+				dAtA8[j7] = uint8(uint64(num)&0x7f | 0x80)
+				num >>= 7
+				j7++
+			}
+			dAtA8[j7] = uint8(num)
+			j7++
+		}
+		i -= j7
+		copy(dAtA[i:], dAtA8[:j7])
+		i = encodeVarintEvents(dAtA, i, uint64(j7))
+		i--
+		dAtA[i] = 0x1a
+	}
+	if m.GlobalVirtualGroupFamilyId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.GlobalVirtualGroupFamilyId))
+		i--
+		dAtA[i] = 0x10
+	}
+	if m.StorageProviderId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.StorageProviderId))
+		i--
+		dAtA[i] = 0x8
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *EventStorageProviderExit) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *EventStorageProviderExit) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *EventStorageProviderExit) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.OperatorAddress) > 0 {
+		i -= len(m.OperatorAddress)
+		copy(dAtA[i:], m.OperatorAddress)
+		i = encodeVarintEvents(dAtA, i, uint64(len(m.OperatorAddress)))
+		i--
+		dAtA[i] = 0x12
+	}
+	if m.StorageProviderId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.StorageProviderId))
+		i--
+		dAtA[i] = 0x8
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *EventCompleteStorageProviderExit) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *EventCompleteStorageProviderExit) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *EventCompleteStorageProviderExit) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	{
+		size := m.TotalDeposit.Size()
+		i -= size
+		if _, err := m.TotalDeposit.MarshalTo(dAtA[i:]); err != nil {
+			return 0, err
+		}
+		i = encodeVarintEvents(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0x1a
+	if len(m.OperatorAddress) > 0 {
+		i -= len(m.OperatorAddress)
+		copy(dAtA[i:], m.OperatorAddress)
+		i = encodeVarintEvents(dAtA, i, uint64(len(m.OperatorAddress)))
+		i--
+		dAtA[i] = 0x12
+	}
+	if m.StorageProviderId != 0 {
+		i = encodeVarintEvents(dAtA, i, uint64(m.StorageProviderId))
+		i--
+		dAtA[i] = 0x8
+	}
+	return len(dAtA) - i, nil
+}
+
+func encodeVarintEvents(dAtA []byte, offset int, v uint64) int {
+	offset -= sovEvents(v)
+	base := offset
+	for v >= 1<<7 {
+		dAtA[offset] = uint8(v&0x7f | 0x80)
+		v >>= 7
+		offset++
+	}
+	dAtA[offset] = uint8(v)
+	return base
+}
+func (m *EventCreateGlobalVirtualGroup) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.Id != 0 {
+		n += 1 + sovEvents(uint64(m.Id))
+	}
+	if m.FamilyId != 0 {
+		n += 1 + sovEvents(uint64(m.FamilyId))
+	}
+	if m.PrimarySpId != 0 {
+		n += 1 + sovEvents(uint64(m.PrimarySpId))
+	}
+	if len(m.SecondarySpIds) > 0 {
+		l = 0
+		for _, e := range m.SecondarySpIds {
+			l += sovEvents(uint64(e))
+		}
+		n += 1 + sovEvents(uint64(l)) + l
+	}
+	if m.StoredSize != 0 {
+		n += 1 + sovEvents(uint64(m.StoredSize))
+	}
+	l = len(m.VirtualPaymentAddress)
+	if l > 0 {
+		n += 1 + l + sovEvents(uint64(l))
+	}
+	l = m.TotalDeposit.Size()
+	n += 1 + l + sovEvents(uint64(l))
+	return n
+}
+
+func (m *EventCreateGlobalVirtualGroupFamily) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.Id != 0 {
+		n += 1 + sovEvents(uint64(m.Id))
+	}
+	l = len(m.VirtualPaymentAddress)
+	if l > 0 {
+		n += 1 + l + sovEvents(uint64(l))
+	}
+	return n
+}
+
+func (m *EventDeleteGlobalVirtualGroup) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.Id != 0 {
+		n += 1 + sovEvents(uint64(m.Id))
+	}
+	return n
+}
+
+func (m *EventUpdateGlobalVirtualGroup) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.Id != 0 {
+		n += 1 + sovEvents(uint64(m.Id))
+	}
+	if m.StoreSize != 0 {
+		n += 1 + sovEvents(uint64(m.StoreSize))
+	}
+	l = m.TotalDeposit.Size()
+	n += 1 + l + sovEvents(uint64(l))
+	return n
+}
+
+func (m *EventCreateLocalVirtualGroup) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.Id != 0 {
+		n += 1 + sovEvents(uint64(m.Id))
+	}
+	l = m.BucketId.Size()
+	n += 1 + l + sovEvents(uint64(l))
+	if m.GlobalVirtualGroupId != 0 {
+		n += 1 + sovEvents(uint64(m.GlobalVirtualGroupId))
+	}
+	if m.StoredSize != 0 {
+		n += 1 + sovEvents(uint64(m.StoredSize))
+	}
+	return n
+}
+
+func (m *EventUpdateLocalVirtualGroup) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.Id != 0 {
+		n += 1 + sovEvents(uint64(m.Id))
+	}
+	if m.GlobalVirtualGroupId != 0 {
+		n += 1 + sovEvents(uint64(m.GlobalVirtualGroupId))
+	}
+	if m.StoredSize != 0 {
+		n += 1 + sovEvents(uint64(m.StoredSize))
+	}
+	return n
+}
+
+func (m *EventSwapOut) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.StorageProviderId != 0 {
+		n += 1 + sovEvents(uint64(m.StorageProviderId))
+	}
+	if m.GlobalVirtualGroupFamilyId != 0 {
+		n += 1 + sovEvents(uint64(m.GlobalVirtualGroupFamilyId))
+	}
+	if len(m.GlobalVirtualGroupIds) > 0 {
+		l = 0
+		for _, e := range m.GlobalVirtualGroupIds {
+			l += sovEvents(uint64(e))
+		}
+		n += 1 + sovEvents(uint64(l)) + l
+	}
+	if m.SuccessorSpId != 0 {
+		n += 1 + sovEvents(uint64(m.SuccessorSpId))
+	}
+	return n
+}
+
+func (m *EventCompleteSwapOut) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.StorageProviderId != 0 {
+		n += 1 + sovEvents(uint64(m.StorageProviderId))
+	}
+	if m.SrcStorageProviderId != 0 {
+		n += 1 + sovEvents(uint64(m.SrcStorageProviderId))
+	}
+	if m.GlobalVirtualGroupFamilyId != 0 {
+		n += 1 + sovEvents(uint64(m.GlobalVirtualGroupFamilyId))
+	}
+	if len(m.GlobalVirtualGroupIds) > 0 {
+		l = 0
+		for _, e := range m.GlobalVirtualGroupIds {
+			l += sovEvents(uint64(e))
+		}
+		n += 1 + sovEvents(uint64(l)) + l
+	}
+	return n
+}
+
+func (m *EventCancelSwapOut) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.StorageProviderId != 0 {
+		n += 1 + sovEvents(uint64(m.StorageProviderId))
+	}
+	if m.GlobalVirtualGroupFamilyId != 0 {
+		n += 1 + sovEvents(uint64(m.GlobalVirtualGroupFamilyId))
+	}
+	if len(m.GlobalVirtualGroupIds) > 0 {
+		l = 0
+		for _, e := range m.GlobalVirtualGroupIds {
+			l += sovEvents(uint64(e))
+		}
+		n += 1 + sovEvents(uint64(l)) + l
+	}
+	if m.SuccessorSpId != 0 {
+		n += 1 + sovEvents(uint64(m.SuccessorSpId))
+	}
+	return n
+}
+
+func (m *EventStorageProviderExit) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.StorageProviderId != 0 {
+		n += 1 + sovEvents(uint64(m.StorageProviderId))
+	}
+	l = len(m.OperatorAddress)
+	if l > 0 {
+		n += 1 + l + sovEvents(uint64(l))
+	}
+	return n
+}
+
+func (m *EventCompleteStorageProviderExit) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.StorageProviderId != 0 {
+		n += 1 + sovEvents(uint64(m.StorageProviderId))
+	}
+	l = len(m.OperatorAddress)
+	if l > 0 {
+		n += 1 + l + sovEvents(uint64(l))
+	}
+	l = m.TotalDeposit.Size()
+	n += 1 + l + sovEvents(uint64(l))
+	return n
+}
+
+func sovEvents(x uint64) (n int) {
+	return (math_bits.Len64(x|1) + 6) / 7
+}
+func sozEvents(x uint64) (n int) {
+	return sovEvents(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *EventCreateGlobalVirtualGroup) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowEvents
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: EventCreateGlobalVirtualGroup: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: EventCreateGlobalVirtualGroup: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType)
+			}
+			m.Id = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.Id |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field FamilyId", wireType)
+			}
+			m.FamilyId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.FamilyId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 3:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field PrimarySpId", wireType)
+			}
+			m.PrimarySpId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.PrimarySpId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 4:
+			if wireType == 0 {
+				var v uint32
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowEvents
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					v |= uint32(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				m.SecondarySpIds = append(m.SecondarySpIds, v)
+			} else if wireType == 2 {
+				var packedLen int
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowEvents
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					packedLen |= int(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				if packedLen < 0 {
+					return ErrInvalidLengthEvents
+				}
+				postIndex := iNdEx + packedLen
+				if postIndex < 0 {
+					return ErrInvalidLengthEvents
+				}
+				if postIndex > l {
+					return io.ErrUnexpectedEOF
+				}
+				var elementCount int
+				var count int
+				for _, integer := range dAtA[iNdEx:postIndex] {
+					if integer < 128 {
+						count++
+					}
+				}
+				elementCount = count
+				if elementCount != 0 && len(m.SecondarySpIds) == 0 {
+					m.SecondarySpIds = make([]uint32, 0, elementCount)
+				}
+				for iNdEx < postIndex {
+					var v uint32
+					for shift := uint(0); ; shift += 7 {
+						if shift >= 64 {
+							return ErrIntOverflowEvents
+						}
+						if iNdEx >= l {
+							return io.ErrUnexpectedEOF
+						}
+						b := dAtA[iNdEx]
+						iNdEx++
+						v |= uint32(b&0x7F) << shift
+						if b < 0x80 {
+							break
+						}
+					}
+					m.SecondarySpIds = append(m.SecondarySpIds, v)
+				}
+			} else {
+				return fmt.Errorf("proto: wrong wireType = %d for field SecondarySpIds", wireType)
+			}
+		case 5:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field StoredSize", wireType)
+			}
+			m.StoredSize = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.StoredSize |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 6:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field VirtualPaymentAddress", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthEvents
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.VirtualPaymentAddress = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 7:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field TotalDeposit", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthEvents
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.TotalDeposit.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipEvents(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *EventCreateGlobalVirtualGroupFamily) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowEvents
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: EventCreateGlobalVirtualGroupFamily: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: EventCreateGlobalVirtualGroupFamily: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType)
+			}
+			m.Id = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.Id |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field VirtualPaymentAddress", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthEvents
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.VirtualPaymentAddress = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipEvents(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *EventDeleteGlobalVirtualGroup) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowEvents
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: EventDeleteGlobalVirtualGroup: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: EventDeleteGlobalVirtualGroup: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType)
+			}
+			m.Id = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.Id |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipEvents(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *EventUpdateGlobalVirtualGroup) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowEvents
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: EventUpdateGlobalVirtualGroup: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: EventUpdateGlobalVirtualGroup: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType)
+			}
+			m.Id = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.Id |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field StoreSize", wireType)
+			}
+			m.StoreSize = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.StoreSize |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field TotalDeposit", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthEvents
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.TotalDeposit.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipEvents(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *EventCreateLocalVirtualGroup) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowEvents
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: EventCreateLocalVirtualGroup: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: EventCreateLocalVirtualGroup: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType)
+			}
+			m.Id = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.Id |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field BucketId", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthEvents
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.BucketId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 3:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupId", wireType)
+			}
+			m.GlobalVirtualGroupId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.GlobalVirtualGroupId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 4:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field StoredSize", wireType)
+			}
+			m.StoredSize = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.StoredSize |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipEvents(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *EventUpdateLocalVirtualGroup) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowEvents
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: EventUpdateLocalVirtualGroup: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: EventUpdateLocalVirtualGroup: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType)
+			}
+			m.Id = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.Id |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupId", wireType)
+			}
+			m.GlobalVirtualGroupId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.GlobalVirtualGroupId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 3:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field StoredSize", wireType)
+			}
+			m.StoredSize = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.StoredSize |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipEvents(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *EventSwapOut) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowEvents
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: EventSwapOut: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: EventSwapOut: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field StorageProviderId", wireType)
+			}
+			m.StorageProviderId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.StorageProviderId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupFamilyId", wireType)
+			}
+			m.GlobalVirtualGroupFamilyId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.GlobalVirtualGroupFamilyId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 3:
+			if wireType == 0 {
+				var v uint32
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowEvents
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					v |= uint32(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				m.GlobalVirtualGroupIds = append(m.GlobalVirtualGroupIds, v)
+			} else if wireType == 2 {
+				var packedLen int
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowEvents
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					packedLen |= int(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				if packedLen < 0 {
+					return ErrInvalidLengthEvents
+				}
+				postIndex := iNdEx + packedLen
+				if postIndex < 0 {
+					return ErrInvalidLengthEvents
+				}
+				if postIndex > l {
+					return io.ErrUnexpectedEOF
+				}
+				var elementCount int
+				var count int
+				for _, integer := range dAtA[iNdEx:postIndex] {
+					if integer < 128 {
+						count++
+					}
+				}
+				elementCount = count
+				if elementCount != 0 && len(m.GlobalVirtualGroupIds) == 0 {
+					m.GlobalVirtualGroupIds = make([]uint32, 0, elementCount)
+				}
+				for iNdEx < postIndex {
+					var v uint32
+					for shift := uint(0); ; shift += 7 {
+						if shift >= 64 {
+							return ErrIntOverflowEvents
+						}
+						if iNdEx >= l {
+							return io.ErrUnexpectedEOF
+						}
+						b := dAtA[iNdEx]
+						iNdEx++
+						v |= uint32(b&0x7F) << shift
+						if b < 0x80 {
+							break
+						}
+					}
+					m.GlobalVirtualGroupIds = append(m.GlobalVirtualGroupIds, v)
+				}
+			} else {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupIds", wireType)
+			}
+		case 4:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field SuccessorSpId", wireType)
+			}
+			m.SuccessorSpId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.SuccessorSpId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipEvents(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *EventCompleteSwapOut) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowEvents
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: EventCompleteSwapOut: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: EventCompleteSwapOut: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field StorageProviderId", wireType)
+			}
+			m.StorageProviderId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.StorageProviderId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field SrcStorageProviderId", wireType)
+			}
+			m.SrcStorageProviderId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.SrcStorageProviderId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 3:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupFamilyId", wireType)
+			}
+			m.GlobalVirtualGroupFamilyId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.GlobalVirtualGroupFamilyId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 4:
+			if wireType == 0 {
+				var v uint32
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowEvents
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					v |= uint32(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				m.GlobalVirtualGroupIds = append(m.GlobalVirtualGroupIds, v)
+			} else if wireType == 2 {
+				var packedLen int
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowEvents
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					packedLen |= int(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				if packedLen < 0 {
+					return ErrInvalidLengthEvents
+				}
+				postIndex := iNdEx + packedLen
+				if postIndex < 0 {
+					return ErrInvalidLengthEvents
+				}
+				if postIndex > l {
+					return io.ErrUnexpectedEOF
+				}
+				var elementCount int
+				var count int
+				for _, integer := range dAtA[iNdEx:postIndex] {
+					if integer < 128 {
+						count++
+					}
+				}
+				elementCount = count
+				if elementCount != 0 && len(m.GlobalVirtualGroupIds) == 0 {
+					m.GlobalVirtualGroupIds = make([]uint32, 0, elementCount)
+				}
+				for iNdEx < postIndex {
+					var v uint32
+					for shift := uint(0); ; shift += 7 {
+						if shift >= 64 {
+							return ErrIntOverflowEvents
+						}
+						if iNdEx >= l {
+							return io.ErrUnexpectedEOF
+						}
+						b := dAtA[iNdEx]
+						iNdEx++
+						v |= uint32(b&0x7F) << shift
+						if b < 0x80 {
+							break
+						}
+					}
+					m.GlobalVirtualGroupIds = append(m.GlobalVirtualGroupIds, v)
+				}
+			} else {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupIds", wireType)
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipEvents(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *EventCancelSwapOut) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowEvents
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: EventCancelSwapOut: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: EventCancelSwapOut: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field StorageProviderId", wireType)
+			}
+			m.StorageProviderId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.StorageProviderId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupFamilyId", wireType)
+			}
+			m.GlobalVirtualGroupFamilyId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.GlobalVirtualGroupFamilyId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 3:
+			if wireType == 0 {
+				var v uint32
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowEvents
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					v |= uint32(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				m.GlobalVirtualGroupIds = append(m.GlobalVirtualGroupIds, v)
+			} else if wireType == 2 {
+				var packedLen int
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowEvents
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					packedLen |= int(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				if packedLen < 0 {
+					return ErrInvalidLengthEvents
+				}
+				postIndex := iNdEx + packedLen
+				if postIndex < 0 {
+					return ErrInvalidLengthEvents
+				}
+				if postIndex > l {
+					return io.ErrUnexpectedEOF
+				}
+				var elementCount int
+				var count int
+				for _, integer := range dAtA[iNdEx:postIndex] {
+					if integer < 128 {
+						count++
+					}
+				}
+				elementCount = count
+				if elementCount != 0 && len(m.GlobalVirtualGroupIds) == 0 {
+					m.GlobalVirtualGroupIds = make([]uint32, 0, elementCount)
+				}
+				for iNdEx < postIndex {
+					var v uint32
+					for shift := uint(0); ; shift += 7 {
+						if shift >= 64 {
+							return ErrIntOverflowEvents
+						}
+						if iNdEx >= l {
+							return io.ErrUnexpectedEOF
+						}
+						b := dAtA[iNdEx]
+						iNdEx++
+						v |= uint32(b&0x7F) << shift
+						if b < 0x80 {
+							break
+						}
+					}
+					m.GlobalVirtualGroupIds = append(m.GlobalVirtualGroupIds, v)
+				}
+			} else {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupIds", wireType)
+			}
+		case 4:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field SuccessorSpId", wireType)
+			}
+			m.SuccessorSpId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.SuccessorSpId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipEvents(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *EventStorageProviderExit) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowEvents
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: EventStorageProviderExit: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: EventStorageProviderExit: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field StorageProviderId", wireType)
+			}
+			m.StorageProviderId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.StorageProviderId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field OperatorAddress", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthEvents
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.OperatorAddress = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipEvents(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *EventCompleteStorageProviderExit) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowEvents
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: EventCompleteStorageProviderExit: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: EventCompleteStorageProviderExit: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field StorageProviderId", wireType)
+			}
+			m.StorageProviderId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.StorageProviderId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field OperatorAddress", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthEvents
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.OperatorAddress = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field TotalDeposit", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthEvents
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.TotalDeposit.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipEvents(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func skipEvents(dAtA []byte) (n int, err error) {
+	l := len(dAtA)
+	iNdEx := 0
+	depth := 0
+	for iNdEx < l {
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return 0, ErrIntOverflowEvents
+			}
+			if iNdEx >= l {
+				return 0, io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		wireType := int(wire & 0x7)
+		switch wireType {
+		case 0:
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				iNdEx++
+				if dAtA[iNdEx-1] < 0x80 {
+					break
+				}
+			}
+		case 1:
+			iNdEx += 8
+		case 2:
+			var length int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				length |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if length < 0 {
+				return 0, ErrInvalidLengthEvents
+			}
+			iNdEx += length
+		case 3:
+			depth++
+		case 4:
+			if depth == 0 {
+				return 0, ErrUnexpectedEndOfGroupEvents
+			}
+			depth--
+		case 5:
+			iNdEx += 4
+		default:
+			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+		}
+		if iNdEx < 0 {
+			return 0, ErrInvalidLengthEvents
+		}
+		if depth == 0 {
+			return iNdEx, nil
+		}
+	}
+	return 0, io.ErrUnexpectedEOF
+}
+
+var (
+	ErrInvalidLengthEvents        = fmt.Errorf("proto: negative length found during unmarshaling")
+	ErrIntOverflowEvents          = fmt.Errorf("proto: integer overflow")
+	ErrUnexpectedEndOfGroupEvents = fmt.Errorf("proto: unexpected end of group")
+)
diff --git a/x/virtualgroup/types/expected_keepers.go b/x/virtualgroup/types/expected_keepers.go
new file mode 100644
index 000000000..9018acfda
--- /dev/null
+++ b/x/virtualgroup/types/expected_keepers.go
@@ -0,0 +1,46 @@
+package types
+
+import (
+	sdkmath "cosmossdk.io/math"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
+
+	sptypes "github.com/bnb-chain/greenfield/x/sp/types"
+)
+
+type SpKeeper interface {
+	GetStorageProvider(ctx sdk.Context, id uint32) (sp *sptypes.StorageProvider, found bool)
+	GetStorageProviderByOperatorAddr(ctx sdk.Context, addr sdk.AccAddress) (sp *sptypes.StorageProvider, found bool)
+	GetStorageProviderByFundingAddr(ctx sdk.Context, sealAddr sdk.AccAddress) (sp *sptypes.StorageProvider, found bool)
+	SetStorageProvider(ctx sdk.Context, sp *sptypes.StorageProvider)
+	Exit(ctx sdk.Context, sp *sptypes.StorageProvider) error
+	DepositDenomForSP(ctx sdk.Context) (res string)
+}
+
+// AccountKeeper defines the expected account keeper used for simulations (noalias)
+// Methods imported from account should be defined here
+type AccountKeeper interface {
+	GetAccount(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI
+	IterateAccounts(ctx sdk.Context, process func(authtypes.AccountI) (stop bool))
+	GetModuleAddress(name string) sdk.AccAddress
+	GetModuleAccount(ctx sdk.Context, moduleName string) authtypes.ModuleAccountI
+	SetModuleAccount(sdk.Context, authtypes.ModuleAccountI)
+}
+
+// BankKeeper defines the expected interface needed to retrieve account balances.
+// Methods imported from bank should be defined here
+type BankKeeper interface {
+	GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
+	GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin
+	LockedCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
+	SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
+	SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
+	SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
+	SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error
+}
+
+type PaymentKeeper interface {
+	QueryDynamicBalance(ctx sdk.Context, addr sdk.AccAddress) (amount sdkmath.Int, err error)
+	Withdraw(ctx sdk.Context, fromAddr, toAddr sdk.AccAddress, amount sdkmath.Int) error
+	IsEmptyNetFlow(ctx sdk.Context, account sdk.AccAddress) bool
+}
diff --git a/x/virtualgroup/types/expected_keepers_mocks.go b/x/virtualgroup/types/expected_keepers_mocks.go
new file mode 100644
index 000000000..1377f7c0a
--- /dev/null
+++ b/x/virtualgroup/types/expected_keepers_mocks.go
@@ -0,0 +1,399 @@
+// Code generated by MockGen. DO NOT EDIT.
+// Source: x/virtualgroup/types/expected_keepers.go
+
+// Package types is a generated GoMock package.
+package types
+
+import (
+	reflect "reflect"
+
+	math "cosmossdk.io/math"
+	types "github.com/bnb-chain/greenfield/x/sp/types"
+	types0 "github.com/cosmos/cosmos-sdk/types"
+	types1 "github.com/cosmos/cosmos-sdk/x/auth/types"
+	gomock "github.com/golang/mock/gomock"
+)
+
+// MockSpKeeper is a mock of SpKeeper interface.
+type MockSpKeeper struct {
+	ctrl     *gomock.Controller
+	recorder *MockSpKeeperMockRecorder
+}
+
+// MockSpKeeperMockRecorder is the mock recorder for MockSpKeeper.
+type MockSpKeeperMockRecorder struct {
+	mock *MockSpKeeper
+}
+
+// NewMockSpKeeper creates a new mock instance.
+func NewMockSpKeeper(ctrl *gomock.Controller) *MockSpKeeper {
+	mock := &MockSpKeeper{ctrl: ctrl}
+	mock.recorder = &MockSpKeeperMockRecorder{mock}
+	return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockSpKeeper) EXPECT() *MockSpKeeperMockRecorder {
+	return m.recorder
+}
+
+// DepositDenomForSP mocks base method.
+func (m *MockSpKeeper) DepositDenomForSP(ctx types0.Context) string {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "DepositDenomForSP", ctx)
+	ret0, _ := ret[0].(string)
+	return ret0
+}
+
+// DepositDenomForSP indicates an expected call of DepositDenomForSP.
+func (mr *MockSpKeeperMockRecorder) DepositDenomForSP(ctx interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DepositDenomForSP", reflect.TypeOf((*MockSpKeeper)(nil).DepositDenomForSP), ctx)
+}
+
+// Exit mocks base method.
+func (m *MockSpKeeper) Exit(ctx types0.Context, sp *types.StorageProvider) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Exit", ctx, sp)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// Exit indicates an expected call of Exit.
+func (mr *MockSpKeeperMockRecorder) Exit(ctx, sp interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exit", reflect.TypeOf((*MockSpKeeper)(nil).Exit), ctx, sp)
+}
+
+// GetStorageProvider mocks base method.
+func (m *MockSpKeeper) GetStorageProvider(ctx types0.Context, id uint32) (*types.StorageProvider, bool) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetStorageProvider", ctx, id)
+	ret0, _ := ret[0].(*types.StorageProvider)
+	ret1, _ := ret[1].(bool)
+	return ret0, ret1
+}
+
+// GetStorageProvider indicates an expected call of GetStorageProvider.
+func (mr *MockSpKeeperMockRecorder) GetStorageProvider(ctx, id interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageProvider", reflect.TypeOf((*MockSpKeeper)(nil).GetStorageProvider), ctx, id)
+}
+
+// GetStorageProviderByFundingAddr mocks base method.
+func (m *MockSpKeeper) GetStorageProviderByFundingAddr(ctx types0.Context, sealAddr types0.AccAddress) (*types.StorageProvider, bool) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetStorageProviderByFundingAddr", ctx, sealAddr)
+	ret0, _ := ret[0].(*types.StorageProvider)
+	ret1, _ := ret[1].(bool)
+	return ret0, ret1
+}
+
+// GetStorageProviderByFundingAddr indicates an expected call of GetStorageProviderByFundingAddr.
+func (mr *MockSpKeeperMockRecorder) GetStorageProviderByFundingAddr(ctx, sealAddr interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageProviderByFundingAddr", reflect.TypeOf((*MockSpKeeper)(nil).GetStorageProviderByFundingAddr), ctx, sealAddr)
+}
+
+// GetStorageProviderByOperatorAddr mocks base method.
+func (m *MockSpKeeper) GetStorageProviderByOperatorAddr(ctx types0.Context, addr types0.AccAddress) (*types.StorageProvider, bool) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetStorageProviderByOperatorAddr", ctx, addr)
+	ret0, _ := ret[0].(*types.StorageProvider)
+	ret1, _ := ret[1].(bool)
+	return ret0, ret1
+}
+
+// GetStorageProviderByOperatorAddr indicates an expected call of GetStorageProviderByOperatorAddr.
+func (mr *MockSpKeeperMockRecorder) GetStorageProviderByOperatorAddr(ctx, addr interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageProviderByOperatorAddr", reflect.TypeOf((*MockSpKeeper)(nil).GetStorageProviderByOperatorAddr), ctx, addr)
+}
+
+// SetStorageProvider mocks base method.
+func (m *MockSpKeeper) SetStorageProvider(ctx types0.Context, sp *types.StorageProvider) {
+	m.ctrl.T.Helper()
+	m.ctrl.Call(m, "SetStorageProvider", ctx, sp)
+}
+
+// SetStorageProvider indicates an expected call of SetStorageProvider.
+func (mr *MockSpKeeperMockRecorder) SetStorageProvider(ctx, sp interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetStorageProvider", reflect.TypeOf((*MockSpKeeper)(nil).SetStorageProvider), ctx, sp)
+}
+
+// MockAccountKeeper is a mock of AccountKeeper interface.
+type MockAccountKeeper struct {
+	ctrl     *gomock.Controller
+	recorder *MockAccountKeeperMockRecorder
+}
+
+// MockAccountKeeperMockRecorder is the mock recorder for MockAccountKeeper.
+type MockAccountKeeperMockRecorder struct {
+	mock *MockAccountKeeper
+}
+
+// NewMockAccountKeeper creates a new mock instance.
+func NewMockAccountKeeper(ctrl *gomock.Controller) *MockAccountKeeper {
+	mock := &MockAccountKeeper{ctrl: ctrl}
+	mock.recorder = &MockAccountKeeperMockRecorder{mock}
+	return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockAccountKeeper) EXPECT() *MockAccountKeeperMockRecorder {
+	return m.recorder
+}
+
+// GetAccount mocks base method.
+func (m *MockAccountKeeper) GetAccount(ctx types0.Context, addr types0.AccAddress) types1.AccountI {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetAccount", ctx, addr)
+	ret0, _ := ret[0].(types1.AccountI)
+	return ret0
+}
+
+// GetAccount indicates an expected call of GetAccount.
+func (mr *MockAccountKeeperMockRecorder) GetAccount(ctx, addr interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetAccount), ctx, addr)
+}
+
+// GetModuleAccount mocks base method.
+func (m *MockAccountKeeper) GetModuleAccount(ctx types0.Context, moduleName string) types1.ModuleAccountI {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetModuleAccount", ctx, moduleName)
+	ret0, _ := ret[0].(types1.ModuleAccountI)
+	return ret0
+}
+
+// GetModuleAccount indicates an expected call of GetModuleAccount.
+func (mr *MockAccountKeeperMockRecorder) GetModuleAccount(ctx, moduleName interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetModuleAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetModuleAccount), ctx, moduleName)
+}
+
+// GetModuleAddress mocks base method.
+func (m *MockAccountKeeper) GetModuleAddress(name string) types0.AccAddress {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetModuleAddress", name)
+	ret0, _ := ret[0].(types0.AccAddress)
+	return ret0
+}
+
+// GetModuleAddress indicates an expected call of GetModuleAddress.
+func (mr *MockAccountKeeperMockRecorder) GetModuleAddress(name interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetModuleAddress", reflect.TypeOf((*MockAccountKeeper)(nil).GetModuleAddress), name)
+}
+
+// IterateAccounts mocks base method.
+func (m *MockAccountKeeper) IterateAccounts(ctx types0.Context, process func(types1.AccountI) bool) {
+	m.ctrl.T.Helper()
+	m.ctrl.Call(m, "IterateAccounts", ctx, process)
+}
+
+// IterateAccounts indicates an expected call of IterateAccounts.
+func (mr *MockAccountKeeperMockRecorder) IterateAccounts(ctx, process interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IterateAccounts", reflect.TypeOf((*MockAccountKeeper)(nil).IterateAccounts), ctx, process)
+}
+
+// SetModuleAccount mocks base method.
+func (m *MockAccountKeeper) SetModuleAccount(arg0 types0.Context, arg1 types1.ModuleAccountI) {
+	m.ctrl.T.Helper()
+	m.ctrl.Call(m, "SetModuleAccount", arg0, arg1)
+}
+
+// SetModuleAccount indicates an expected call of SetModuleAccount.
+func (mr *MockAccountKeeperMockRecorder) SetModuleAccount(arg0, arg1 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetModuleAccount", reflect.TypeOf((*MockAccountKeeper)(nil).SetModuleAccount), arg0, arg1)
+}
+
+// MockBankKeeper is a mock of BankKeeper interface.
+type MockBankKeeper struct {
+	ctrl     *gomock.Controller
+	recorder *MockBankKeeperMockRecorder
+}
+
+// MockBankKeeperMockRecorder is the mock recorder for MockBankKeeper.
+type MockBankKeeperMockRecorder struct {
+	mock *MockBankKeeper
+}
+
+// NewMockBankKeeper creates a new mock instance.
+func NewMockBankKeeper(ctrl *gomock.Controller) *MockBankKeeper {
+	mock := &MockBankKeeper{ctrl: ctrl}
+	mock.recorder = &MockBankKeeperMockRecorder{mock}
+	return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockBankKeeper) EXPECT() *MockBankKeeperMockRecorder {
+	return m.recorder
+}
+
+// GetAllBalances mocks base method.
+func (m *MockBankKeeper) GetAllBalances(ctx types0.Context, addr types0.AccAddress) types0.Coins {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetAllBalances", ctx, addr)
+	ret0, _ := ret[0].(types0.Coins)
+	return ret0
+}
+
+// GetAllBalances indicates an expected call of GetAllBalances.
+func (mr *MockBankKeeperMockRecorder) GetAllBalances(ctx, addr interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllBalances", reflect.TypeOf((*MockBankKeeper)(nil).GetAllBalances), ctx, addr)
+}
+
+// GetBalance mocks base method.
+func (m *MockBankKeeper) GetBalance(ctx types0.Context, addr types0.AccAddress, denom string) types0.Coin {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetBalance", ctx, addr, denom)
+	ret0, _ := ret[0].(types0.Coin)
+	return ret0
+}
+
+// GetBalance indicates an expected call of GetBalance.
+func (mr *MockBankKeeperMockRecorder) GetBalance(ctx, addr, denom interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBalance", reflect.TypeOf((*MockBankKeeper)(nil).GetBalance), ctx, addr, denom)
+}
+
+// LockedCoins mocks base method.
+func (m *MockBankKeeper) LockedCoins(ctx types0.Context, addr types0.AccAddress) types0.Coins {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "LockedCoins", ctx, addr)
+	ret0, _ := ret[0].(types0.Coins)
+	return ret0
+}
+
+// LockedCoins indicates an expected call of LockedCoins.
+func (mr *MockBankKeeperMockRecorder) LockedCoins(ctx, addr interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LockedCoins", reflect.TypeOf((*MockBankKeeper)(nil).LockedCoins), ctx, addr)
+}
+
+// SendCoins mocks base method.
+func (m *MockBankKeeper) SendCoins(ctx types0.Context, fromAddr, toAddr types0.AccAddress, amt types0.Coins) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "SendCoins", ctx, fromAddr, toAddr, amt)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// SendCoins indicates an expected call of SendCoins.
+func (mr *MockBankKeeperMockRecorder) SendCoins(ctx, fromAddr, toAddr, amt interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoins", reflect.TypeOf((*MockBankKeeper)(nil).SendCoins), ctx, fromAddr, toAddr, amt)
+}
+
+// SendCoinsFromAccountToModule mocks base method.
+func (m *MockBankKeeper) SendCoinsFromAccountToModule(ctx types0.Context, senderAddr types0.AccAddress, recipientModule string, amt types0.Coins) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "SendCoinsFromAccountToModule", ctx, senderAddr, recipientModule, amt)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// SendCoinsFromAccountToModule indicates an expected call of SendCoinsFromAccountToModule.
+func (mr *MockBankKeeperMockRecorder) SendCoinsFromAccountToModule(ctx, senderAddr, recipientModule, amt interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromAccountToModule", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromAccountToModule), ctx, senderAddr, recipientModule, amt)
+}
+
+// SendCoinsFromModuleToAccount mocks base method.
+func (m *MockBankKeeper) SendCoinsFromModuleToAccount(ctx types0.Context, senderModule string, recipientAddr types0.AccAddress, amt types0.Coins) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "SendCoinsFromModuleToAccount", ctx, senderModule, recipientAddr, amt)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// SendCoinsFromModuleToAccount indicates an expected call of SendCoinsFromModuleToAccount.
+func (mr *MockBankKeeperMockRecorder) SendCoinsFromModuleToAccount(ctx, senderModule, recipientAddr, amt interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromModuleToAccount", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromModuleToAccount), ctx, senderModule, recipientAddr, amt)
+}
+
+// SpendableCoins mocks base method.
+func (m *MockBankKeeper) SpendableCoins(ctx types0.Context, addr types0.AccAddress) types0.Coins {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "SpendableCoins", ctx, addr)
+	ret0, _ := ret[0].(types0.Coins)
+	return ret0
+}
+
+// SpendableCoins indicates an expected call of SpendableCoins.
+func (mr *MockBankKeeperMockRecorder) SpendableCoins(ctx, addr interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpendableCoins", reflect.TypeOf((*MockBankKeeper)(nil).SpendableCoins), ctx, addr)
+}
+
+// MockPaymentKeeper is a mock of PaymentKeeper interface.
+type MockPaymentKeeper struct {
+	ctrl     *gomock.Controller
+	recorder *MockPaymentKeeperMockRecorder
+}
+
+// MockPaymentKeeperMockRecorder is the mock recorder for MockPaymentKeeper.
+type MockPaymentKeeperMockRecorder struct {
+	mock *MockPaymentKeeper
+}
+
+// NewMockPaymentKeeper creates a new mock instance.
+func NewMockPaymentKeeper(ctrl *gomock.Controller) *MockPaymentKeeper {
+	mock := &MockPaymentKeeper{ctrl: ctrl}
+	mock.recorder = &MockPaymentKeeperMockRecorder{mock}
+	return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockPaymentKeeper) EXPECT() *MockPaymentKeeperMockRecorder {
+	return m.recorder
+}
+
+// IsEmptyNetFlow mocks base method.
+func (m *MockPaymentKeeper) IsEmptyNetFlow(ctx types0.Context, account types0.AccAddress) bool {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "IsEmptyNetFlow", ctx, account)
+	ret0, _ := ret[0].(bool)
+	return ret0
+}
+
+// IsEmptyNetFlow indicates an expected call of IsEmptyNetFlow.
+func (mr *MockPaymentKeeperMockRecorder) IsEmptyNetFlow(ctx, account interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsEmptyNetFlow", reflect.TypeOf((*MockPaymentKeeper)(nil).IsEmptyNetFlow), ctx, account)
+}
+
+// QueryDynamicBalance mocks base method.
+func (m *MockPaymentKeeper) QueryDynamicBalance(ctx types0.Context, addr types0.AccAddress) (math.Int, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "QueryDynamicBalance", ctx, addr)
+	ret0, _ := ret[0].(math.Int)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// QueryDynamicBalance indicates an expected call of QueryDynamicBalance.
+func (mr *MockPaymentKeeperMockRecorder) QueryDynamicBalance(ctx, addr interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryDynamicBalance", reflect.TypeOf((*MockPaymentKeeper)(nil).QueryDynamicBalance), ctx, addr)
+}
+
+// Withdraw mocks base method.
+func (m *MockPaymentKeeper) Withdraw(ctx types0.Context, fromAddr, toAddr types0.AccAddress, amount math.Int) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Withdraw", ctx, fromAddr, toAddr, amount)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// Withdraw indicates an expected call of Withdraw.
+func (mr *MockPaymentKeeperMockRecorder) Withdraw(ctx, fromAddr, toAddr, amount interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Withdraw", reflect.TypeOf((*MockPaymentKeeper)(nil).Withdraw), ctx, fromAddr, toAddr, amount)
+}
diff --git a/x/virtualgroup/types/genesis.go b/x/virtualgroup/types/genesis.go
new file mode 100644
index 000000000..e523553eb
--- /dev/null
+++ b/x/virtualgroup/types/genesis.go
@@ -0,0 +1,19 @@
+package types
+
+// DefaultIndex is the default global index
+const DefaultIndex uint64 = 1
+
+// DefaultGenesis returns the default genesis state
+func DefaultGenesis() *GenesisState {
+	return &GenesisState{
+		// this line is used by starport scaffolding # genesis/types/default
+		Params: DefaultParams(),
+	}
+}
+
+// Validate performs basic genesis state validation returning an error upon any
+// failure.
+func (gs GenesisState) Validate() error {
+	// this line is used by starport scaffolding # genesis/types/validate
+	return gs.Params.Validate()
+}
diff --git a/x/virtualgroup/types/genesis.pb.go b/x/virtualgroup/types/genesis.pb.go
new file mode 100644
index 000000000..da8aa036d
--- /dev/null
+++ b/x/virtualgroup/types/genesis.pb.go
@@ -0,0 +1,324 @@
+// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// source: greenfield/virtualgroup/genesis.proto
+
+package types
+
+import (
+	fmt "fmt"
+	_ "github.com/cosmos/gogoproto/gogoproto"
+	proto "github.com/cosmos/gogoproto/proto"
+	io "io"
+	math "math"
+	math_bits "math/bits"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
+
+// GenesisState defines the virtualgroup module's genesis state.
+// GenesisState defines the raw genesis transaction in JSON.
+type GenesisState struct {
+	Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"`
+}
+
+func (m *GenesisState) Reset()         { *m = GenesisState{} }
+func (m *GenesisState) String() string { return proto.CompactTextString(m) }
+func (*GenesisState) ProtoMessage()    {}
+func (*GenesisState) Descriptor() ([]byte, []int) {
+	return fileDescriptor_6fece737ab6fcb58, []int{0}
+}
+func (m *GenesisState) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_GenesisState.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *GenesisState) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_GenesisState.Merge(m, src)
+}
+func (m *GenesisState) XXX_Size() int {
+	return m.Size()
+}
+func (m *GenesisState) XXX_DiscardUnknown() {
+	xxx_messageInfo_GenesisState.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_GenesisState proto.InternalMessageInfo
+
+func (m *GenesisState) GetParams() Params {
+	if m != nil {
+		return m.Params
+	}
+	return Params{}
+}
+
+func init() {
+	proto.RegisterType((*GenesisState)(nil), "greenfield.virtualgroup.GenesisState")
+}
+
+func init() {
+	proto.RegisterFile("greenfield/virtualgroup/genesis.proto", fileDescriptor_6fece737ab6fcb58)
+}
+
+var fileDescriptor_6fece737ab6fcb58 = []byte{
+	// 204 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4d, 0x2f, 0x4a, 0x4d,
+	0xcd, 0x4b, 0xcb, 0x4c, 0xcd, 0x49, 0xd1, 0x2f, 0xcb, 0x2c, 0x2a, 0x29, 0x4d, 0xcc, 0x49, 0x2f,
+	0xca, 0x2f, 0x2d, 0xd0, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f,
+	0xc9, 0x17, 0x12, 0x47, 0x28, 0xd3, 0x43, 0x56, 0x26, 0x25, 0x92, 0x9e, 0x9f, 0x9e, 0x0f, 0x56,
+	0xa3, 0x0f, 0x62, 0x41, 0x94, 0x4b, 0xa9, 0xe0, 0x32, 0xb5, 0x20, 0xb1, 0x28, 0x31, 0x17, 0x6a,
+	0xa8, 0x92, 0x2f, 0x17, 0x8f, 0x3b, 0xc4, 0x96, 0xe0, 0x92, 0xc4, 0x92, 0x54, 0x21, 0x5b, 0x2e,
+	0x36, 0x88, 0xbc, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0xb7, 0x91, 0xbc, 0x1e, 0x0e, 0x5b, 0xf5, 0x02,
+	0xc0, 0xca, 0x9c, 0x58, 0x4e, 0xdc, 0x93, 0x67, 0x08, 0x82, 0x6a, 0x72, 0xf2, 0x3b, 0xf1, 0x48,
+	0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4, 0x18, 0x27, 0x3c, 0x96, 0x63, 0xb8, 0xf0,
+	0x58, 0x8e, 0xe1, 0xc6, 0x63, 0x39, 0x86, 0x28, 0x93, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd,
+	0xe4, 0xfc, 0x5c, 0xfd, 0xa4, 0xbc, 0x24, 0xdd, 0xe4, 0x8c, 0xc4, 0xcc, 0x3c, 0x7d, 0x24, 0x37,
+	0x56, 0xa0, 0xba, 0xb2, 0xa4, 0xb2, 0x20, 0xb5, 0x38, 0x89, 0x0d, 0xec, 0x4a, 0x63, 0x40, 0x00,
+	0x00, 0x00, 0xff, 0xff, 0x86, 0x6f, 0xc0, 0x2a, 0x23, 0x01, 0x00, 0x00,
+}
+
+func (m *GenesisState) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *GenesisState) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	{
+		size, err := m.Params.MarshalToSizedBuffer(dAtA[:i])
+		if err != nil {
+			return 0, err
+		}
+		i -= size
+		i = encodeVarintGenesis(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0xa
+	return len(dAtA) - i, nil
+}
+
+func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int {
+	offset -= sovGenesis(v)
+	base := offset
+	for v >= 1<<7 {
+		dAtA[offset] = uint8(v&0x7f | 0x80)
+		v >>= 7
+		offset++
+	}
+	dAtA[offset] = uint8(v)
+	return base
+}
+func (m *GenesisState) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = m.Params.Size()
+	n += 1 + l + sovGenesis(uint64(l))
+	return n
+}
+
+func sovGenesis(x uint64) (n int) {
+	return (math_bits.Len64(x|1) + 6) / 7
+}
+func sozGenesis(x uint64) (n int) {
+	return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *GenesisState) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowGenesis
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: GenesisState: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowGenesis
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthGenesis
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthGenesis
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipGenesis(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthGenesis
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func skipGenesis(dAtA []byte) (n int, err error) {
+	l := len(dAtA)
+	iNdEx := 0
+	depth := 0
+	for iNdEx < l {
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return 0, ErrIntOverflowGenesis
+			}
+			if iNdEx >= l {
+				return 0, io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		wireType := int(wire & 0x7)
+		switch wireType {
+		case 0:
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowGenesis
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				iNdEx++
+				if dAtA[iNdEx-1] < 0x80 {
+					break
+				}
+			}
+		case 1:
+			iNdEx += 8
+		case 2:
+			var length int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowGenesis
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				length |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if length < 0 {
+				return 0, ErrInvalidLengthGenesis
+			}
+			iNdEx += length
+		case 3:
+			depth++
+		case 4:
+			if depth == 0 {
+				return 0, ErrUnexpectedEndOfGroupGenesis
+			}
+			depth--
+		case 5:
+			iNdEx += 4
+		default:
+			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+		}
+		if iNdEx < 0 {
+			return 0, ErrInvalidLengthGenesis
+		}
+		if depth == 0 {
+			return iNdEx, nil
+		}
+	}
+	return 0, io.ErrUnexpectedEOF
+}
+
+var (
+	ErrInvalidLengthGenesis        = fmt.Errorf("proto: negative length found during unmarshaling")
+	ErrIntOverflowGenesis          = fmt.Errorf("proto: integer overflow")
+	ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group")
+)
diff --git a/x/virtualgroup/types/keys.go b/x/virtualgroup/types/keys.go
new file mode 100644
index 000000000..88272ece8
--- /dev/null
+++ b/x/virtualgroup/types/keys.go
@@ -0,0 +1,76 @@
+package types
+
+import (
+	"github.com/bnb-chain/greenfield/internal/sequence"
+)
+
+const (
+	// ModuleName defines the module name
+	ModuleName = "virtualgroup"
+
+	// StoreKey defines the primary module store key
+	StoreKey = ModuleName
+
+	// RouterKey defines the module's message routing key
+	RouterKey = ModuleName
+
+	// MemStoreKey defines the in-memory store key
+	MemStoreKey = "mem_virtualgroup"
+
+	// TStoreKey defines the transient store key
+	TStoreKey = "transient_storage"
+
+	// GVGVirtualPaymentAccountName string for derive the virtual payment account for GVG
+	GVGVirtualPaymentAccountName = "global_virtual_group"
+
+	// GVGFamilyName string for derive the virtual payment account for GVG family
+	GVGFamilyName = "global_virtual_group_family"
+
+	// NoSpecifiedFamilyId defines
+	NoSpecifiedFamilyId = uint32(0)
+)
+
+var (
+	ParamsKey = []byte{0x01}
+
+	GVGKey       = []byte{0x21}
+	GVGFamilyKey = []byte{0x22}
+
+	GVGSequencePrefix       = []byte{0x32}
+	GVGFamilySequencePrefix = []byte{0x33}
+
+	GVGStatisticsWithinSPKey = []byte{0x41}
+
+	SwapOutFamilyKey = []byte{0x51}
+	SwapOutGVGKey    = []byte{0x61}
+)
+
+func GetGVGKey(gvgID uint32) []byte {
+	var uint32Seq sequence.Sequence[uint32]
+	return append(GVGKey, uint32Seq.EncodeSequence(gvgID)...)
+}
+
+func GetGVGFamilyKey(spID uint32, familyID uint32) []byte {
+	var uint32Seq sequence.Sequence[uint32]
+	return append(GVGFamilyKey, append(uint32Seq.EncodeSequence(spID), uint32Seq.EncodeSequence(familyID)...)...)
+}
+
+func GetGVGFamilyPrefixKey(spID uint32) []byte {
+	var uint32Seq sequence.Sequence[uint32]
+	return append(GVGFamilyKey, uint32Seq.EncodeSequence(spID)...)
+}
+
+func GetGVGStatisticsWithinSPKey(spID uint32) []byte {
+	var uint32Seq sequence.Sequence[uint32]
+	return append(GVGStatisticsWithinSPKey, uint32Seq.EncodeSequence(spID)...)
+}
+
+func GetSwapOutFamilyKey(globalVirtualGroupFamilyID uint32) []byte {
+	var uint32Seq sequence.Sequence[uint32]
+	return append(SwapOutFamilyKey, uint32Seq.EncodeSequence(globalVirtualGroupFamilyID)...)
+}
+
+func GetSwapOutGVGKey(globalVirtualGroupID uint32) []byte {
+	var uint32Seq sequence.Sequence[uint32]
+	return append(SwapOutGVGKey, uint32Seq.EncodeSequence(globalVirtualGroupID)...)
+}
diff --git a/x/virtualgroup/types/message.go b/x/virtualgroup/types/message.go
new file mode 100644
index 000000000..dac1e3184
--- /dev/null
+++ b/x/virtualgroup/types/message.go
@@ -0,0 +1,323 @@
+package types
+
+import (
+	"cosmossdk.io/errors"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	"github.com/cosmos/gogoproto/proto"
+
+	gnfderrors "github.com/bnb-chain/greenfield/types/errors"
+)
+
+const (
+	TypeMsgCreateGlobalVirtualGroup = "create_global_virtual_group"
+	TypeMsgDeleteGlobalVirtualGroup = "delete_global_virtual_group"
+	TypeMsgDeposit                  = "deposit"
+	TypeMsgWithdraw                 = "withdraw"
+	TypeMsgSwapOut                  = "swap_out"
+	TypeMsgUpdateParams             = "update_params"
+	TypeMsgSettle                   = "settle"
+)
+
+var (
+	_ sdk.Msg = &MsgCreateGlobalVirtualGroup{}
+	_ sdk.Msg = &MsgDeleteGlobalVirtualGroup{}
+	_ sdk.Msg = &MsgDeposit{}
+	_ sdk.Msg = &MsgWithdraw{}
+	_ sdk.Msg = &MsgSwapOut{}
+	_ sdk.Msg = &MsgUpdateParams{}
+	_ sdk.Msg = &MsgSettle{}
+)
+
+func NewMsgCreateGlobalVirtualGroup(primarySpAddress sdk.AccAddress, globalVirtualFamilyId uint32, secondarySpIds []uint32, deposit sdk.Coin) *MsgCreateGlobalVirtualGroup {
+	return &MsgCreateGlobalVirtualGroup{
+		StorageProvider: primarySpAddress.String(),
+		FamilyId:        globalVirtualFamilyId,
+		SecondarySpIds:  secondarySpIds,
+		Deposit:         deposit,
+	}
+}
+
+func (msg *MsgCreateGlobalVirtualGroup) Route() string {
+	return RouterKey
+}
+
+func (msg *MsgCreateGlobalVirtualGroup) Type() string {
+	return TypeMsgCreateGlobalVirtualGroup
+}
+
+// GetSignBytes implements the LegacyMsg interface.
+func (msg *MsgCreateGlobalVirtualGroup) GetSignBytes() []byte {
+	return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg))
+}
+
+// GetSigners returns the expected signers for a MsgUpdateParams message.
+func (msg *MsgCreateGlobalVirtualGroup) GetSigners() []sdk.AccAddress {
+	addr, err := sdk.AccAddressFromHexUnsafe(msg.StorageProvider)
+	if err != nil {
+		panic(err)
+	}
+	return []sdk.AccAddress{addr}
+}
+
+// ValidateBasic does a sanity check on the provided data.
+func (msg *MsgCreateGlobalVirtualGroup) ValidateBasic() error {
+	_, err := sdk.AccAddressFromHexUnsafe(msg.StorageProvider)
+	if err != nil {
+		return err
+	}
+
+	if !msg.Deposit.IsValid() || !msg.Deposit.Amount.IsPositive() {
+		return errors.Wrap(sdkerrors.ErrInvalidRequest, "invalid deposit amount")
+	}
+
+	return nil
+}
+
+func NewMsgDeleteGlobalVirtualGroup(primarySpAddress sdk.AccAddress, globalVirtualGroupID uint32) *MsgDeleteGlobalVirtualGroup {
+	return &MsgDeleteGlobalVirtualGroup{
+		StorageProvider:      primarySpAddress.String(),
+		GlobalVirtualGroupId: globalVirtualGroupID,
+	}
+}
+
+func (msg *MsgDeleteGlobalVirtualGroup) Route() string {
+	return RouterKey
+}
+
+func (msg *MsgDeleteGlobalVirtualGroup) Type() string {
+	return TypeMsgDeleteGlobalVirtualGroup
+}
+
+// GetSignBytes implements the LegacyMsg interface.
+func (msg *MsgDeleteGlobalVirtualGroup) GetSignBytes() []byte {
+	return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg))
+}
+
+// GetSigners returns the expected signers for a MsgUpdateParams message.
+func (msg *MsgDeleteGlobalVirtualGroup) GetSigners() []sdk.AccAddress {
+	addr, err := sdk.AccAddressFromHexUnsafe(msg.StorageProvider)
+	if err != nil {
+		panic(err)
+	}
+	return []sdk.AccAddress{addr}
+}
+
+// ValidateBasic does a sanity check on the provided data.
+func (msg *MsgDeleteGlobalVirtualGroup) ValidateBasic() error {
+	_, err := sdk.AccAddressFromHexUnsafe(msg.StorageProvider)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func NewMsgDeposit(fundingAddress sdk.AccAddress, globalVirtualGroupID uint32, deposit sdk.Coin) *MsgDeposit {
+	return &MsgDeposit{
+		StorageProvider:      fundingAddress.String(),
+		GlobalVirtualGroupId: globalVirtualGroupID,
+		Deposit:              deposit,
+	}
+}
+
+func (msg *MsgDeposit) Route() string {
+	return RouterKey
+}
+
+func (msg *MsgDeposit) Type() string {
+	return TypeMsgDeposit
+}
+
+// GetSignBytes implements the LegacyMsg interface.
+func (msg *MsgDeposit) GetSignBytes() []byte {
+	return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg))
+}
+
+// GetSigners returns the expected signers for a MsgUpdateParams message.
+func (msg *MsgDeposit) GetSigners() []sdk.AccAddress {
+	addr, err := sdk.AccAddressFromHexUnsafe(msg.StorageProvider)
+	if err != nil {
+		panic(err)
+	}
+	return []sdk.AccAddress{addr}
+}
+
+// ValidateBasic does a sanity check on the provided data.
+func (msg *MsgDeposit) ValidateBasic() error {
+	_, err := sdk.AccAddressFromHexUnsafe(msg.StorageProvider)
+	if err != nil {
+		return err
+	}
+
+	if !msg.Deposit.IsValid() || !msg.Deposit.Amount.IsPositive() {
+		return errors.Wrap(sdkerrors.ErrInvalidRequest, "invalid deposit amount")
+	}
+
+	return nil
+}
+
+func NewMsgWithdraw(fundingAddress sdk.AccAddress, globalVirtualGroupID uint32, withdraw sdk.Coin) *MsgWithdraw {
+	return &MsgWithdraw{
+		StorageProvider:      fundingAddress.String(),
+		GlobalVirtualGroupId: globalVirtualGroupID,
+		Withdraw:             withdraw,
+	}
+}
+
+func (msg *MsgWithdraw) Route() string {
+	return RouterKey
+}
+
+func (msg *MsgWithdraw) Type() string {
+	return TypeMsgWithdraw
+}
+
+// GetSignBytes implements the LegacyMsg interface.
+func (msg *MsgWithdraw) GetSignBytes() []byte {
+	return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg))
+}
+
+// GetSigners returns the expected signers for a MsgUpdateParams message.
+func (msg *MsgWithdraw) GetSigners() []sdk.AccAddress {
+	addr, _ := sdk.AccAddressFromHexUnsafe(msg.StorageProvider)
+	return []sdk.AccAddress{addr}
+}
+
+// ValidateBasic does a sanity check on the provided data.
+func (msg *MsgWithdraw) ValidateBasic() error {
+	_, err := sdk.AccAddressFromHexUnsafe(msg.StorageProvider)
+	if err != nil {
+		return err
+	}
+
+	if !msg.Withdraw.IsValid() || !msg.Withdraw.Amount.IsPositive() {
+		return errors.Wrap(sdkerrors.ErrInvalidRequest, "invalid or non-positive deposit amount")
+	}
+	return nil
+}
+
+func NewMsgSwapOut(operatorAddress sdk.AccAddress, globalVirtualGroupFamilyID uint32, globalVirtualGroupIDs []uint32, successorSPID uint32) *MsgSwapOut {
+	return &MsgSwapOut{
+		StorageProvider:            operatorAddress.String(),
+		GlobalVirtualGroupFamilyId: globalVirtualGroupFamilyID,
+		GlobalVirtualGroupIds:      globalVirtualGroupIDs,
+		SuccessorSpId:              successorSPID,
+	}
+}
+
+func (msg *MsgSwapOut) Route() string {
+	return RouterKey
+}
+
+func (msg *MsgSwapOut) Type() string {
+	return TypeMsgSwapOut
+}
+
+// GetSignBytes implements the LegacyMsg interface.
+func (msg *MsgSwapOut) GetSignBytes() []byte {
+	return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg))
+}
+
+func (msg *MsgSwapOut) GetApprovalBytes() []byte {
+	fakeMsg := proto.Clone(msg).(*MsgSwapOut)
+	fakeMsg.SuccessorSpApproval.Sig = nil
+	return fakeMsg.GetSignBytes()
+}
+
+// GetSigners returns the expected signers for a MsgUpdateParams message.
+func (msg *MsgSwapOut) GetSigners() []sdk.AccAddress {
+	addr, _ := sdk.AccAddressFromHexUnsafe(msg.StorageProvider)
+	return []sdk.AccAddress{addr}
+}
+
+// ValidateBasic does a sanity check on the provided data.
+func (msg *MsgSwapOut) ValidateBasic() error {
+	_, err := sdk.AccAddressFromHexUnsafe(msg.StorageProvider)
+	if err != nil {
+		return err
+	}
+
+	if msg.GlobalVirtualGroupFamilyId == NoSpecifiedFamilyId {
+		if len(msg.GlobalVirtualGroupIds) == 0 {
+			return gnfderrors.ErrInvalidMessage.Wrap("The gvgs are not allowed to be empty when familyID is not specified.")
+		}
+	} else {
+		if len(msg.GlobalVirtualGroupIds) > 0 {
+			return gnfderrors.ErrInvalidMessage.Wrap("The gvgs are not allowed to be non-empty when familyID is specified.")
+		}
+	}
+
+	if msg.SuccessorSpId == 0 {
+		return gnfderrors.ErrInvalidMessage.Wrap("The successor sp id is not specify.")
+	}
+
+	return nil
+}
+
+// GetSignBytes implements the LegacyMsg interface.
+func (msg *MsgUpdateParams) GetSignBytes() []byte {
+	return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg))
+}
+
+// GetSigners returns the expected signers for a MsgUpdateParams message.
+func (msg *MsgUpdateParams) GetSigners() []sdk.AccAddress {
+	addr, _ := sdk.AccAddressFromHexUnsafe(msg.Authority)
+	return []sdk.AccAddress{addr}
+}
+
+// ValidateBasic does a sanity check on the provided data.
+func (msg *MsgUpdateParams) ValidateBasic() error {
+	if _, err := sdk.AccAddressFromHexUnsafe(msg.Authority); err != nil {
+		return errors.Wrap(err, "invalid authority address")
+	}
+
+	if err := msg.Params.Validate(); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func NewMsgSettle(fundingAddress sdk.AccAddress, globalVirtualGroupFamilyID uint32, globalVirtualGroupIDs []uint32) *MsgSettle {
+	return &MsgSettle{
+		StorageProvider:            fundingAddress.String(),
+		GlobalVirtualGroupFamilyId: globalVirtualGroupFamilyID,
+		GlobalVirtualGroupIds:      globalVirtualGroupIDs,
+	}
+}
+
+func (msg *MsgSettle) Route() string {
+	return RouterKey
+}
+
+func (msg *MsgSettle) Type() string {
+	return TypeMsgSettle
+}
+
+// GetSignBytes implements the LegacyMsg interface.
+func (msg *MsgSettle) GetSignBytes() []byte {
+	return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg))
+}
+
+// GetSigners returns the expected signers for a MsgUpdateParams message.
+func (msg *MsgSettle) GetSigners() []sdk.AccAddress {
+	addr, _ := sdk.AccAddressFromHexUnsafe(msg.StorageProvider)
+	return []sdk.AccAddress{addr}
+}
+
+// ValidateBasic does a sanity check on the provided data.
+func (msg *MsgSettle) ValidateBasic() error {
+	_, err := sdk.AccAddressFromHexUnsafe(msg.StorageProvider)
+	if err != nil {
+		return err
+	}
+
+	if msg.GlobalVirtualGroupFamilyId == NoSpecifiedFamilyId {
+		if len(msg.GlobalVirtualGroupIds) == 0 || len(msg.GlobalVirtualGroupIds) > 10 {
+			return ErrInvalidGVGCount
+		}
+	}
+
+	return nil
+}
diff --git a/x/virtualgroup/types/message_cancel_swap_out.go b/x/virtualgroup/types/message_cancel_swap_out.go
new file mode 100644
index 000000000..5f803d6da
--- /dev/null
+++ b/x/virtualgroup/types/message_cancel_swap_out.go
@@ -0,0 +1,58 @@
+package types
+
+import (
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+
+	gnfderrors "github.com/bnb-chain/greenfield/types/errors"
+)
+
+const TypeMsgCancelSwapOut = "cancel_swap_out"
+
+var _ sdk.Msg = &MsgCancelSwapOut{}
+
+func NewMsgCancelSwapOut(storageProvider sdk.AccAddress, globalVirtualGroupFamilyID uint32, globalVirtualGroupIDs []uint32) *MsgCancelSwapOut {
+	return &MsgCancelSwapOut{
+		StorageProvider:            storageProvider.String(),
+		GlobalVirtualGroupFamilyId: globalVirtualGroupFamilyID,
+		GlobalVirtualGroupIds:      globalVirtualGroupIDs,
+	}
+}
+
+func (msg *MsgCancelSwapOut) Route() string {
+	return RouterKey
+}
+
+func (msg *MsgCancelSwapOut) Type() string {
+	return TypeMsgCancelSwapOut
+}
+
+func (msg *MsgCancelSwapOut) GetSigners() []sdk.AccAddress {
+	creator, err := sdk.AccAddressFromHexUnsafe(msg.StorageProvider)
+	if err != nil {
+		panic(err)
+	}
+	return []sdk.AccAddress{creator}
+}
+
+func (msg *MsgCancelSwapOut) GetSignBytes() []byte {
+	bz := ModuleCdc.MustMarshalJSON(msg)
+	return sdk.MustSortJSON(bz)
+}
+
+func (msg *MsgCancelSwapOut) ValidateBasic() error {
+	_, err := sdk.AccAddressFromHexUnsafe(msg.StorageProvider)
+	if err != nil {
+		return sdkerrors.ErrInvalidAddress.Wrapf("invalid creator address (%s)", err)
+	}
+	if msg.GlobalVirtualGroupFamilyId == NoSpecifiedFamilyId {
+		if len(msg.GlobalVirtualGroupIds) == 0 {
+			return gnfderrors.ErrInvalidMessage.Wrap("The gvgs are not allowed to be empty when familyID is not specified.")
+		}
+	} else {
+		if len(msg.GlobalVirtualGroupIds) > 0 {
+			return gnfderrors.ErrInvalidMessage.Wrap("The gvgs are not allowed to be non-empty when familyID is specified.")
+		}
+	}
+	return nil
+}
diff --git a/x/virtualgroup/types/message_cancel_swap_out_test.go b/x/virtualgroup/types/message_cancel_swap_out_test.go
new file mode 100644
index 000000000..5aa4d79f6
--- /dev/null
+++ b/x/virtualgroup/types/message_cancel_swap_out_test.go
@@ -0,0 +1,43 @@
+package types
+
+import (
+	"testing"
+
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	"github.com/stretchr/testify/require"
+
+	"github.com/bnb-chain/greenfield/testutil/sample"
+)
+
+func TestMsgCancelSwapOut_ValidateBasic(t *testing.T) {
+	tests := []struct {
+		name string
+		msg  MsgCancelSwapOut
+		err  error
+	}{
+		{
+			name: "invalid address",
+			msg: MsgCancelSwapOut{
+				StorageProvider:            "invalid_address",
+				GlobalVirtualGroupFamilyId: 1,
+			},
+			err: sdkerrors.ErrInvalidAddress,
+		}, {
+			name: "valid address",
+			msg: MsgCancelSwapOut{
+				StorageProvider:            sample.AccAddress(),
+				GlobalVirtualGroupFamilyId: 1,
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			err := tt.msg.ValidateBasic()
+			if tt.err != nil {
+				require.ErrorIs(t, err, tt.err)
+				return
+			}
+			require.NoError(t, err)
+		})
+	}
+}
diff --git a/x/virtualgroup/types/message_complete_storage_provider_exit.go b/x/virtualgroup/types/message_complete_storage_provider_exit.go
new file mode 100644
index 000000000..9dc038f93
--- /dev/null
+++ b/x/virtualgroup/types/message_complete_storage_provider_exit.go
@@ -0,0 +1,46 @@
+package types
+
+import (
+	"cosmossdk.io/errors"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+)
+
+const TypeMsgCompleteStorageProviderExit = "complete_storage_provider_exit"
+
+var _ sdk.Msg = &MsgCompleteStorageProviderExit{}
+
+func NewMsgCompleteStorageProviderExit(operator sdk.AccAddress) *MsgCompleteStorageProviderExit {
+	return &MsgCompleteStorageProviderExit{
+		StorageProvider: operator.String(),
+	}
+}
+
+func (msg *MsgCompleteStorageProviderExit) Route() string {
+	return RouterKey
+}
+
+func (msg *MsgCompleteStorageProviderExit) Type() string {
+	return TypeMsgCompleteStorageProviderExit
+}
+
+func (msg *MsgCompleteStorageProviderExit) GetSigners() []sdk.AccAddress {
+	creator, err := sdk.AccAddressFromHexUnsafe(msg.StorageProvider)
+	if err != nil {
+		panic(err)
+	}
+	return []sdk.AccAddress{creator}
+}
+
+func (msg *MsgCompleteStorageProviderExit) GetSignBytes() []byte {
+	bz := ModuleCdc.MustMarshalJSON(msg)
+	return sdk.MustSortJSON(bz)
+}
+
+func (msg *MsgCompleteStorageProviderExit) ValidateBasic() error {
+	_, err := sdk.AccAddressFromHexUnsafe(msg.StorageProvider)
+	if err != nil {
+		return errors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid creator address (%s)", err)
+	}
+	return nil
+}
diff --git a/x/virtualgroup/types/message_complete_storage_provider_exit_test.go b/x/virtualgroup/types/message_complete_storage_provider_exit_test.go
new file mode 100644
index 000000000..97b94fedc
--- /dev/null
+++ b/x/virtualgroup/types/message_complete_storage_provider_exit_test.go
@@ -0,0 +1,41 @@
+package types
+
+import (
+	"testing"
+
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	"github.com/stretchr/testify/require"
+
+	"github.com/bnb-chain/greenfield/testutil/sample"
+)
+
+func TestMsgCompleteStorageProviderExit_ValidateBasic(t *testing.T) {
+	tests := []struct {
+		name string
+		msg  MsgCompleteStorageProviderExit
+		err  error
+	}{
+		{
+			name: "invalid address",
+			msg: MsgCompleteStorageProviderExit{
+				StorageProvider: "invalid_address",
+			},
+			err: sdkerrors.ErrInvalidAddress,
+		}, {
+			name: "valid address",
+			msg: MsgCompleteStorageProviderExit{
+				StorageProvider: sample.AccAddress(),
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			err := tt.msg.ValidateBasic()
+			if tt.err != nil {
+				require.ErrorIs(t, err, tt.err)
+				return
+			}
+			require.NoError(t, err)
+		})
+	}
+}
diff --git a/x/virtualgroup/types/message_complete_swap_out.go b/x/virtualgroup/types/message_complete_swap_out.go
new file mode 100644
index 000000000..415c662e4
--- /dev/null
+++ b/x/virtualgroup/types/message_complete_swap_out.go
@@ -0,0 +1,58 @@
+package types
+
+import (
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+
+	gnfderrors "github.com/bnb-chain/greenfield/types/errors"
+)
+
+const TypeMsgCompleteSwapOut = "complete_swap_out"
+
+var _ sdk.Msg = &MsgCompleteSwapOut{}
+
+func NewMsgCompleteSwapOut(storageProvider sdk.AccAddress, globalVirtualGroupFamilyID uint32, globalVirtualGroupIDs []uint32) *MsgCompleteSwapOut {
+	return &MsgCompleteSwapOut{
+		StorageProvider:            storageProvider.String(),
+		GlobalVirtualGroupFamilyId: globalVirtualGroupFamilyID,
+		GlobalVirtualGroupIds:      globalVirtualGroupIDs,
+	}
+}
+
+func (msg *MsgCompleteSwapOut) Route() string {
+	return RouterKey
+}
+
+func (msg *MsgCompleteSwapOut) Type() string {
+	return TypeMsgCompleteSwapOut
+}
+
+func (msg *MsgCompleteSwapOut) GetSigners() []sdk.AccAddress {
+	creator, err := sdk.AccAddressFromHexUnsafe(msg.StorageProvider)
+	if err != nil {
+		panic(err)
+	}
+	return []sdk.AccAddress{creator}
+}
+
+func (msg *MsgCompleteSwapOut) GetSignBytes() []byte {
+	bz := ModuleCdc.MustMarshalJSON(msg)
+	return sdk.MustSortJSON(bz)
+}
+
+func (msg *MsgCompleteSwapOut) ValidateBasic() error {
+	_, err := sdk.AccAddressFromHexUnsafe(msg.StorageProvider)
+	if err != nil {
+		return sdkerrors.ErrInvalidAddress.Wrapf("invalid creator address (%s)", err)
+	}
+	if msg.GlobalVirtualGroupFamilyId == NoSpecifiedFamilyId {
+		if len(msg.GlobalVirtualGroupIds) == 0 {
+			return gnfderrors.ErrInvalidMessage.Wrap("The gvgs are not allowed to be empty when familyID is not specified.")
+		}
+	} else {
+		if len(msg.GlobalVirtualGroupIds) > 0 {
+			return gnfderrors.ErrInvalidMessage.Wrap("The gvgs are not allowed to be non-empty when familyID is specified.")
+		}
+	}
+	return nil
+}
diff --git a/x/virtualgroup/types/message_complete_swap_out_test.go b/x/virtualgroup/types/message_complete_swap_out_test.go
new file mode 100644
index 000000000..7d17fcd8f
--- /dev/null
+++ b/x/virtualgroup/types/message_complete_swap_out_test.go
@@ -0,0 +1,43 @@
+package types
+
+import (
+	"testing"
+
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	"github.com/stretchr/testify/require"
+
+	"github.com/bnb-chain/greenfield/testutil/sample"
+)
+
+func TestMsgCompleteSwapOut_ValidateBasic(t *testing.T) {
+	tests := []struct {
+		name string
+		msg  MsgCompleteSwapOut
+		err  error
+	}{
+		{
+			name: "invalid address",
+			msg: MsgCompleteSwapOut{
+				StorageProvider:            "invalid_address",
+				GlobalVirtualGroupFamilyId: 1,
+			},
+			err: sdkerrors.ErrInvalidAddress,
+		}, {
+			name: "valid address",
+			msg: MsgCompleteSwapOut{
+				StorageProvider:            sample.AccAddress(),
+				GlobalVirtualGroupFamilyId: 1,
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			err := tt.msg.ValidateBasic()
+			if tt.err != nil {
+				require.ErrorIs(t, err, tt.err)
+				return
+			}
+			require.NoError(t, err)
+		})
+	}
+}
diff --git a/x/virtualgroup/types/message_storage_provider_exit.go b/x/virtualgroup/types/message_storage_provider_exit.go
new file mode 100644
index 000000000..9e0c178c7
--- /dev/null
+++ b/x/virtualgroup/types/message_storage_provider_exit.go
@@ -0,0 +1,46 @@
+package types
+
+import (
+	"cosmossdk.io/errors"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+)
+
+const TypeMsgStorageProviderExit = "storage_provider_exit"
+
+var _ sdk.Msg = &MsgStorageProviderExit{}
+
+func NewMsgStorageProviderExit(operatorAddress sdk.AccAddress) *MsgStorageProviderExit {
+	return &MsgStorageProviderExit{
+		StorageProvider: operatorAddress.String(),
+	}
+}
+
+func (msg *MsgStorageProviderExit) Route() string {
+	return RouterKey
+}
+
+func (msg *MsgStorageProviderExit) Type() string {
+	return TypeMsgStorageProviderExit
+}
+
+func (msg *MsgStorageProviderExit) GetSigners() []sdk.AccAddress {
+	creator, err := sdk.AccAddressFromHexUnsafe(msg.StorageProvider)
+	if err != nil {
+		panic(err)
+	}
+	return []sdk.AccAddress{creator}
+}
+
+func (msg *MsgStorageProviderExit) GetSignBytes() []byte {
+	bz := ModuleCdc.MustMarshalJSON(msg)
+	return sdk.MustSortJSON(bz)
+}
+
+func (msg *MsgStorageProviderExit) ValidateBasic() error {
+	_, err := sdk.AccAddressFromHexUnsafe(msg.StorageProvider)
+	if err != nil {
+		return errors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid creator address (%s)", err)
+	}
+	return nil
+}
diff --git a/x/virtualgroup/types/message_storage_provider_exit_test.go b/x/virtualgroup/types/message_storage_provider_exit_test.go
new file mode 100644
index 000000000..49e2e2b57
--- /dev/null
+++ b/x/virtualgroup/types/message_storage_provider_exit_test.go
@@ -0,0 +1,41 @@
+package types
+
+import (
+	"testing"
+
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	"github.com/stretchr/testify/require"
+
+	"github.com/bnb-chain/greenfield/testutil/sample"
+)
+
+func TestMsgStorageProviderExit_ValidateBasic(t *testing.T) {
+	tests := []struct {
+		name string
+		msg  MsgStorageProviderExit
+		err  error
+	}{
+		{
+			name: "invalid address",
+			msg: MsgStorageProviderExit{
+				StorageProvider: "invalid_address",
+			},
+			err: sdkerrors.ErrInvalidAddress,
+		}, {
+			name: "valid address",
+			msg: MsgStorageProviderExit{
+				StorageProvider: sample.AccAddress(),
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			err := tt.msg.ValidateBasic()
+			if tt.err != nil {
+				require.ErrorIs(t, err, tt.err)
+				return
+			}
+			require.NoError(t, err)
+		})
+	}
+}
diff --git a/x/virtualgroup/types/params.go b/x/virtualgroup/types/params.go
new file mode 100644
index 000000000..4ee62fa37
--- /dev/null
+++ b/x/virtualgroup/types/params.go
@@ -0,0 +1,127 @@
+package types
+
+import (
+	"errors"
+	"fmt"
+	"strings"
+
+	"cosmossdk.io/math"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
+	"gopkg.in/yaml.v2"
+)
+
+const (
+	// DefaultDepositDenom Dafault deposit denom
+	DefaultDepositDenom = "BNB"
+)
+
+var (
+	// DefaultGVGStakingPerBytes defines the default gvg staking price
+	DefaultGVGStakingPerBytes                = sdk.NewInt(16000) // 20%~30% of store price
+	DefaultMaxGlobalVirtualGroupNumPerFamily = uint32(10)
+	DefaultMaxStoreSizePerFamily             = uint64(64) * 1024 * 1024 * 1024 * 1024 //64T
+
+	KeyDepositDenom                      = []byte("DepositDenom")
+	KeyGVGStakingPerBytes                = []byte("GVGStakingPerBytes")
+	KeyMaxGlobalVirtualGroupNumPerFamily = []byte("MaxGlobalVirtualGroupNumPerFamily")
+	KeyMaxStoreSizePerFamily             = []byte("MaxStoreSizePerFamily")
+)
+
+var _ paramtypes.ParamSet = (*Params)(nil)
+
+// ParamKeyTable the param key table for launch module
+func ParamKeyTable() paramtypes.KeyTable {
+	return paramtypes.NewKeyTable().RegisterParamSet(&Params{})
+}
+
+// NewParams creates a new Params instance
+func NewParams(depositDenom string, gvgStakingPerBytes math.Int, maxGlobalVirtualGroupPerFamily uint32,
+	maxStoreSizePerFamily uint64) Params {
+	return Params{
+		DepositDenom:                      depositDenom,
+		GvgStakingPerBytes:                gvgStakingPerBytes,
+		MaxGlobalVirtualGroupNumPerFamily: maxGlobalVirtualGroupPerFamily,
+		MaxStoreSizePerFamily:             maxStoreSizePerFamily,
+	}
+}
+
+// DefaultParams returns a default set of parameters
+func DefaultParams() Params {
+	return NewParams(DefaultDepositDenom, DefaultGVGStakingPerBytes, DefaultMaxGlobalVirtualGroupNumPerFamily, DefaultMaxStoreSizePerFamily)
+}
+
+// ParamSetPairs get the params.ParamSet
+func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs {
+	return paramtypes.ParamSetPairs{
+		paramtypes.NewParamSetPair(KeyDepositDenom, &p.DepositDenom, validateDepositDenom),
+		paramtypes.NewParamSetPair(KeyGVGStakingPerBytes, &p.GvgStakingPerBytes, validateGVGStakingPerBytes),
+		paramtypes.NewParamSetPair(KeyMaxGlobalVirtualGroupNumPerFamily, &p.MaxGlobalVirtualGroupNumPerFamily, validateMaxGlobalVirtualGroupNumPerFamily),
+		paramtypes.NewParamSetPair(KeyMaxStoreSizePerFamily, &p.MaxStoreSizePerFamily, validateMaxStoreSizePerFamily),
+	}
+}
+
+// Validate validates the set of params
+func (p Params) Validate() error {
+	return nil
+}
+
+// String implements the Stringer interface.
+func (p Params) String() string {
+	out, _ := yaml.Marshal(p)
+	return string(out)
+}
+
+func validateDepositDenom(i interface{}) error {
+	v, ok := i.(string)
+	if !ok {
+		return fmt.Errorf("invalid parameter type: %T", i)
+	}
+
+	if strings.TrimSpace(v) == "" {
+		return errors.New("deposit denom cannot be blank")
+	}
+
+	if err := sdk.ValidateDenom(v); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func validateGVGStakingPerBytes(i interface{}) error {
+	v, ok := i.(sdk.Dec)
+	if !ok {
+		return fmt.Errorf("invalid parameter type: %T", i)
+	}
+	if v.IsNil() || !v.IsPositive() || v.GT(sdk.OneDec()) {
+		return fmt.Errorf("invalid secondary sp store price ratio")
+	}
+	return nil
+}
+
+func validateMaxGlobalVirtualGroupNumPerFamily(i interface{}) error {
+	v, ok := i.(uint32)
+	if !ok {
+		return fmt.Errorf("invalid parameter type: %T", i)
+	}
+
+	if v == 0 {
+		return fmt.Errorf("max buckets per account must be positive: %d", v)
+	}
+
+	return nil
+}
+
+func validateMaxStoreSizePerFamily(i interface{}) error {
+	v, ok := i.(uint64)
+	if !ok {
+		return fmt.Errorf("invalid parameter type: %T", i)
+	}
+
+	if v == 0 {
+		return fmt.Errorf("max buckets per account must be positive: %d", v)
+	}
+
+	return nil
+}
diff --git a/x/virtualgroup/types/params.pb.go b/x/virtualgroup/types/params.pb.go
new file mode 100644
index 000000000..4f0e126cd
--- /dev/null
+++ b/x/virtualgroup/types/params.pb.go
@@ -0,0 +1,528 @@
+// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// source: greenfield/virtualgroup/params.proto
+
+package types
+
+import (
+	fmt "fmt"
+	_ "github.com/cosmos/cosmos-proto"
+	github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types"
+	_ "github.com/cosmos/gogoproto/gogoproto"
+	proto "github.com/cosmos/gogoproto/proto"
+	io "io"
+	math "math"
+	math_bits "math/bits"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
+
+// Params defines the parameters for the module.
+type Params struct {
+	// deposit_denom defines the staking coin denomination.
+	DepositDenom string `protobuf:"bytes,1,opt,name=deposit_denom,json=depositDenom,proto3" json:"deposit_denom,omitempty"`
+	// store price, in bnb wei per charge byte
+	GvgStakingPerBytes github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,2,opt,name=gvg_staking_per_bytes,json=gvgStakingPerBytes,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"gvg_staking_per_bytes"`
+	// the max number of lvg which allowed in a bucket
+	MaxLocalVirtualGroupNumPerBucket uint32 `protobuf:"varint,3,opt,name=max_local_virtual_group_num_per_bucket,json=maxLocalVirtualGroupNumPerBucket,proto3" json:"max_local_virtual_group_num_per_bucket,omitempty"`
+	// the max number of gvg which can exist in a family
+	MaxGlobalVirtualGroupNumPerFamily uint32 `protobuf:"varint,4,opt,name=max_global_virtual_group_num_per_family,json=maxGlobalVirtualGroupNumPerFamily,proto3" json:"max_global_virtual_group_num_per_family,omitempty"`
+	// if the store size reach the exceed, the family is not allowed to sever more buckets
+	MaxStoreSizePerFamily uint64 `protobuf:"varint,5,opt,name=max_store_size_per_family,json=maxStoreSizePerFamily,proto3" json:"max_store_size_per_family,omitempty"`
+}
+
+func (m *Params) Reset()      { *m = Params{} }
+func (*Params) ProtoMessage() {}
+func (*Params) Descriptor() ([]byte, []int) {
+	return fileDescriptor_d8ecf89dd5128885, []int{0}
+}
+func (m *Params) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_Params.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *Params) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Params.Merge(m, src)
+}
+func (m *Params) XXX_Size() int {
+	return m.Size()
+}
+func (m *Params) XXX_DiscardUnknown() {
+	xxx_messageInfo_Params.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Params proto.InternalMessageInfo
+
+func (m *Params) GetDepositDenom() string {
+	if m != nil {
+		return m.DepositDenom
+	}
+	return ""
+}
+
+func (m *Params) GetMaxLocalVirtualGroupNumPerBucket() uint32 {
+	if m != nil {
+		return m.MaxLocalVirtualGroupNumPerBucket
+	}
+	return 0
+}
+
+func (m *Params) GetMaxGlobalVirtualGroupNumPerFamily() uint32 {
+	if m != nil {
+		return m.MaxGlobalVirtualGroupNumPerFamily
+	}
+	return 0
+}
+
+func (m *Params) GetMaxStoreSizePerFamily() uint64 {
+	if m != nil {
+		return m.MaxStoreSizePerFamily
+	}
+	return 0
+}
+
+func init() {
+	proto.RegisterType((*Params)(nil), "greenfield.virtualgroup.Params")
+}
+
+func init() {
+	proto.RegisterFile("greenfield/virtualgroup/params.proto", fileDescriptor_d8ecf89dd5128885)
+}
+
+var fileDescriptor_d8ecf89dd5128885 = []byte{
+	// 414 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0x3f, 0x6f, 0xd4, 0x30,
+	0x18, 0x87, 0x63, 0x5a, 0x2a, 0xb0, 0xe8, 0x12, 0x51, 0x91, 0x76, 0x48, 0xc2, 0x1f, 0x95, 0x5b,
+	0x2e, 0x19, 0x60, 0x40, 0x88, 0xe9, 0x84, 0xa8, 0x2a, 0xa1, 0xea, 0x94, 0x93, 0x18, 0x58, 0x2c,
+	0x27, 0x71, 0x5d, 0xeb, 0x62, 0x3b, 0xb2, 0x9d, 0x53, 0xae, 0x9f, 0x80, 0x91, 0x91, 0xb1, 0x1f,
+	0x82, 0x0f, 0x71, 0xe3, 0x89, 0x09, 0x31, 0x9c, 0xd0, 0xdd, 0xc2, 0xc7, 0x40, 0x76, 0x22, 0x08,
+	0x42, 0x9d, 0x92, 0xfc, 0xf2, 0xf8, 0xd1, 0xfb, 0xbe, 0x7e, 0xe1, 0x33, 0xaa, 0x08, 0x11, 0x97,
+	0x8c, 0x54, 0x65, 0xba, 0x60, 0xca, 0x34, 0xb8, 0xa2, 0x4a, 0x36, 0x75, 0x5a, 0x63, 0x85, 0xb9,
+	0x4e, 0x6a, 0x25, 0x8d, 0xf4, 0x1f, 0xfd, 0xa5, 0x92, 0x21, 0x75, 0x72, 0x5c, 0x48, 0xcd, 0xa5,
+	0x46, 0x0e, 0x4b, 0xbb, 0x8f, 0xee, 0xcc, 0xc9, 0x43, 0x2a, 0xa9, 0xec, 0x72, 0xfb, 0xd6, 0xa5,
+	0x4f, 0x3e, 0xed, 0xc1, 0x83, 0xa9, 0x53, 0xfb, 0x4f, 0xe1, 0x61, 0x49, 0x6a, 0xa9, 0x99, 0x41,
+	0x25, 0x11, 0x92, 0x07, 0x20, 0x06, 0xa3, 0xfb, 0xd9, 0x83, 0x3e, 0x7c, 0x6b, 0x33, 0x5f, 0xc2,
+	0x23, 0xba, 0xa0, 0x48, 0x1b, 0x3c, 0x67, 0x82, 0xa2, 0x9a, 0x28, 0x94, 0x2f, 0x0d, 0xd1, 0xc1,
+	0x1d, 0x0b, 0x4f, 0xde, 0xac, 0x36, 0x91, 0xf7, 0x63, 0x13, 0x9d, 0x52, 0x66, 0xae, 0x9a, 0x3c,
+	0x29, 0x24, 0xef, 0xab, 0xe8, 0x1f, 0x63, 0x5d, 0xce, 0x53, 0xb3, 0xac, 0x89, 0x4e, 0xce, 0x85,
+	0xf9, 0xf6, 0x75, 0x0c, 0xfb, 0x22, 0xcf, 0x85, 0xc9, 0x7c, 0xba, 0xa0, 0xb3, 0xce, 0x3c, 0x25,
+	0x6a, 0x62, 0xbd, 0xfe, 0x14, 0x9e, 0x72, 0xdc, 0xa2, 0x4a, 0x16, 0xb8, 0x42, 0x7d, 0xaf, 0xc8,
+	0x35, 0x8b, 0x44, 0xc3, 0xbb, 0x02, 0x9a, 0x62, 0x4e, 0x4c, 0xb0, 0x17, 0x83, 0xd1, 0x61, 0x16,
+	0x73, 0xdc, 0xbe, 0xb7, 0xf0, 0x87, 0x8e, 0x3d, 0xb3, 0xe8, 0x45, 0xc3, 0xad, 0xd0, 0x71, 0x7e,
+	0x06, 0x9f, 0x5b, 0x23, 0xad, 0x64, 0x7e, 0xab, 0xf2, 0x12, 0x73, 0x56, 0x2d, 0x83, 0x7d, 0xa7,
+	0x7c, 0xcc, 0x71, 0x7b, 0xe6, 0xe8, 0xff, 0x9d, 0xef, 0x1c, 0xe8, 0xbf, 0x82, 0xc7, 0xd6, 0xa9,
+	0x8d, 0x54, 0x04, 0x69, 0x76, 0x4d, 0x86, 0x96, 0xbb, 0x31, 0x18, 0xed, 0x67, 0x47, 0x1c, 0xb7,
+	0x33, 0xfb, 0x7f, 0xc6, 0xae, 0xc9, 0x9f, 0x93, 0xaf, 0xef, 0x7d, 0xb9, 0x89, 0xbc, 0x5f, 0x37,
+	0x11, 0x98, 0x5c, 0xac, 0xb6, 0x21, 0x58, 0x6f, 0x43, 0xf0, 0x73, 0x1b, 0x82, 0xcf, 0xbb, 0xd0,
+	0x5b, 0xef, 0x42, 0xef, 0xfb, 0x2e, 0xf4, 0x3e, 0xbe, 0x1c, 0x4c, 0x33, 0x17, 0xf9, 0xb8, 0xb8,
+	0xc2, 0x4c, 0xa4, 0x83, 0x4d, 0x69, 0xff, 0xdd, 0x15, 0x37, 0xdf, 0xfc, 0xc0, 0xdd, 0xf0, 0x8b,
+	0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x12, 0x99, 0xe2, 0xe9, 0x53, 0x02, 0x00, 0x00,
+}
+
+func (this *Params) Equal(that interface{}) bool {
+	if that == nil {
+		return this == nil
+	}
+
+	that1, ok := that.(*Params)
+	if !ok {
+		that2, ok := that.(Params)
+		if ok {
+			that1 = &that2
+		} else {
+			return false
+		}
+	}
+	if that1 == nil {
+		return this == nil
+	} else if this == nil {
+		return false
+	}
+	if this.DepositDenom != that1.DepositDenom {
+		return false
+	}
+	if !this.GvgStakingPerBytes.Equal(that1.GvgStakingPerBytes) {
+		return false
+	}
+	if this.MaxLocalVirtualGroupNumPerBucket != that1.MaxLocalVirtualGroupNumPerBucket {
+		return false
+	}
+	if this.MaxGlobalVirtualGroupNumPerFamily != that1.MaxGlobalVirtualGroupNumPerFamily {
+		return false
+	}
+	if this.MaxStoreSizePerFamily != that1.MaxStoreSizePerFamily {
+		return false
+	}
+	return true
+}
+func (m *Params) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *Params) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.MaxStoreSizePerFamily != 0 {
+		i = encodeVarintParams(dAtA, i, uint64(m.MaxStoreSizePerFamily))
+		i--
+		dAtA[i] = 0x28
+	}
+	if m.MaxGlobalVirtualGroupNumPerFamily != 0 {
+		i = encodeVarintParams(dAtA, i, uint64(m.MaxGlobalVirtualGroupNumPerFamily))
+		i--
+		dAtA[i] = 0x20
+	}
+	if m.MaxLocalVirtualGroupNumPerBucket != 0 {
+		i = encodeVarintParams(dAtA, i, uint64(m.MaxLocalVirtualGroupNumPerBucket))
+		i--
+		dAtA[i] = 0x18
+	}
+	{
+		size := m.GvgStakingPerBytes.Size()
+		i -= size
+		if _, err := m.GvgStakingPerBytes.MarshalTo(dAtA[i:]); err != nil {
+			return 0, err
+		}
+		i = encodeVarintParams(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0x12
+	if len(m.DepositDenom) > 0 {
+		i -= len(m.DepositDenom)
+		copy(dAtA[i:], m.DepositDenom)
+		i = encodeVarintParams(dAtA, i, uint64(len(m.DepositDenom)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func encodeVarintParams(dAtA []byte, offset int, v uint64) int {
+	offset -= sovParams(v)
+	base := offset
+	for v >= 1<<7 {
+		dAtA[offset] = uint8(v&0x7f | 0x80)
+		v >>= 7
+		offset++
+	}
+	dAtA[offset] = uint8(v)
+	return base
+}
+func (m *Params) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.DepositDenom)
+	if l > 0 {
+		n += 1 + l + sovParams(uint64(l))
+	}
+	l = m.GvgStakingPerBytes.Size()
+	n += 1 + l + sovParams(uint64(l))
+	if m.MaxLocalVirtualGroupNumPerBucket != 0 {
+		n += 1 + sovParams(uint64(m.MaxLocalVirtualGroupNumPerBucket))
+	}
+	if m.MaxGlobalVirtualGroupNumPerFamily != 0 {
+		n += 1 + sovParams(uint64(m.MaxGlobalVirtualGroupNumPerFamily))
+	}
+	if m.MaxStoreSizePerFamily != 0 {
+		n += 1 + sovParams(uint64(m.MaxStoreSizePerFamily))
+	}
+	return n
+}
+
+func sovParams(x uint64) (n int) {
+	return (math_bits.Len64(x|1) + 6) / 7
+}
+func sozParams(x uint64) (n int) {
+	return sovParams(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *Params) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowParams
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: Params: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field DepositDenom", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowParams
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthParams
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthParams
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.DepositDenom = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GvgStakingPerBytes", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowParams
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthParams
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthParams
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.GvgStakingPerBytes.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 3:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field MaxLocalVirtualGroupNumPerBucket", wireType)
+			}
+			m.MaxLocalVirtualGroupNumPerBucket = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowParams
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.MaxLocalVirtualGroupNumPerBucket |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 4:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field MaxGlobalVirtualGroupNumPerFamily", wireType)
+			}
+			m.MaxGlobalVirtualGroupNumPerFamily = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowParams
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.MaxGlobalVirtualGroupNumPerFamily |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 5:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field MaxStoreSizePerFamily", wireType)
+			}
+			m.MaxStoreSizePerFamily = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowParams
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.MaxStoreSizePerFamily |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipParams(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthParams
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func skipParams(dAtA []byte) (n int, err error) {
+	l := len(dAtA)
+	iNdEx := 0
+	depth := 0
+	for iNdEx < l {
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return 0, ErrIntOverflowParams
+			}
+			if iNdEx >= l {
+				return 0, io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		wireType := int(wire & 0x7)
+		switch wireType {
+		case 0:
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowParams
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				iNdEx++
+				if dAtA[iNdEx-1] < 0x80 {
+					break
+				}
+			}
+		case 1:
+			iNdEx += 8
+		case 2:
+			var length int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowParams
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				length |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if length < 0 {
+				return 0, ErrInvalidLengthParams
+			}
+			iNdEx += length
+		case 3:
+			depth++
+		case 4:
+			if depth == 0 {
+				return 0, ErrUnexpectedEndOfGroupParams
+			}
+			depth--
+		case 5:
+			iNdEx += 4
+		default:
+			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+		}
+		if iNdEx < 0 {
+			return 0, ErrInvalidLengthParams
+		}
+		if depth == 0 {
+			return iNdEx, nil
+		}
+	}
+	return 0, io.ErrUnexpectedEOF
+}
+
+var (
+	ErrInvalidLengthParams        = fmt.Errorf("proto: negative length found during unmarshaling")
+	ErrIntOverflowParams          = fmt.Errorf("proto: integer overflow")
+	ErrUnexpectedEndOfGroupParams = fmt.Errorf("proto: unexpected end of group")
+)
diff --git a/x/virtualgroup/types/query.pb.go b/x/virtualgroup/types/query.pb.go
new file mode 100644
index 000000000..6f994875a
--- /dev/null
+++ b/x/virtualgroup/types/query.pb.go
@@ -0,0 +1,2254 @@
+// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// source: greenfield/virtualgroup/query.proto
+
+package types
+
+import (
+	context "context"
+	fmt "fmt"
+	query "github.com/cosmos/cosmos-sdk/types/query"
+	_ "github.com/cosmos/gogoproto/gogoproto"
+	grpc1 "github.com/cosmos/gogoproto/grpc"
+	proto "github.com/cosmos/gogoproto/proto"
+	_ "google.golang.org/genproto/googleapis/api/annotations"
+	grpc "google.golang.org/grpc"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+	io "io"
+	math "math"
+	math_bits "math/bits"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
+
+// QueryParamsRequest is request type for the Query/Params RPC method.
+type QueryParamsRequest struct {
+}
+
+func (m *QueryParamsRequest) Reset()         { *m = QueryParamsRequest{} }
+func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) }
+func (*QueryParamsRequest) ProtoMessage()    {}
+func (*QueryParamsRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_83cd53fc415e00e7, []int{0}
+}
+func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *QueryParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_QueryParamsRequest.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *QueryParamsRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_QueryParamsRequest.Merge(m, src)
+}
+func (m *QueryParamsRequest) XXX_Size() int {
+	return m.Size()
+}
+func (m *QueryParamsRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_QueryParamsRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo
+
+// QueryParamsResponse is response type for the Query/Params RPC method.
+type QueryParamsResponse struct {
+	// params holds all the parameters of this module.
+	Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"`
+}
+
+func (m *QueryParamsResponse) Reset()         { *m = QueryParamsResponse{} }
+func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) }
+func (*QueryParamsResponse) ProtoMessage()    {}
+func (*QueryParamsResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_83cd53fc415e00e7, []int{1}
+}
+func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *QueryParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_QueryParamsResponse.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *QueryParamsResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_QueryParamsResponse.Merge(m, src)
+}
+func (m *QueryParamsResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *QueryParamsResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_QueryParamsResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo
+
+func (m *QueryParamsResponse) GetParams() Params {
+	if m != nil {
+		return m.Params
+	}
+	return Params{}
+}
+
+type QueryGlobalVirtualGroupRequest struct {
+	GlobalVirtualGroupId uint32 `protobuf:"varint,1,opt,name=global_virtual_group_id,json=globalVirtualGroupId,proto3" json:"global_virtual_group_id,omitempty"`
+}
+
+func (m *QueryGlobalVirtualGroupRequest) Reset()         { *m = QueryGlobalVirtualGroupRequest{} }
+func (m *QueryGlobalVirtualGroupRequest) String() string { return proto.CompactTextString(m) }
+func (*QueryGlobalVirtualGroupRequest) ProtoMessage()    {}
+func (*QueryGlobalVirtualGroupRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_83cd53fc415e00e7, []int{2}
+}
+func (m *QueryGlobalVirtualGroupRequest) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *QueryGlobalVirtualGroupRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_QueryGlobalVirtualGroupRequest.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *QueryGlobalVirtualGroupRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_QueryGlobalVirtualGroupRequest.Merge(m, src)
+}
+func (m *QueryGlobalVirtualGroupRequest) XXX_Size() int {
+	return m.Size()
+}
+func (m *QueryGlobalVirtualGroupRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_QueryGlobalVirtualGroupRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryGlobalVirtualGroupRequest proto.InternalMessageInfo
+
+func (m *QueryGlobalVirtualGroupRequest) GetGlobalVirtualGroupId() uint32 {
+	if m != nil {
+		return m.GlobalVirtualGroupId
+	}
+	return 0
+}
+
+type QueryGlobalVirtualGroupResponse struct {
+	GlobalVirtualGroup *GlobalVirtualGroup `protobuf:"bytes,1,opt,name=global_virtual_group,json=globalVirtualGroup,proto3" json:"global_virtual_group,omitempty"`
+}
+
+func (m *QueryGlobalVirtualGroupResponse) Reset()         { *m = QueryGlobalVirtualGroupResponse{} }
+func (m *QueryGlobalVirtualGroupResponse) String() string { return proto.CompactTextString(m) }
+func (*QueryGlobalVirtualGroupResponse) ProtoMessage()    {}
+func (*QueryGlobalVirtualGroupResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_83cd53fc415e00e7, []int{3}
+}
+func (m *QueryGlobalVirtualGroupResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *QueryGlobalVirtualGroupResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_QueryGlobalVirtualGroupResponse.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *QueryGlobalVirtualGroupResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_QueryGlobalVirtualGroupResponse.Merge(m, src)
+}
+func (m *QueryGlobalVirtualGroupResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *QueryGlobalVirtualGroupResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_QueryGlobalVirtualGroupResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryGlobalVirtualGroupResponse proto.InternalMessageInfo
+
+func (m *QueryGlobalVirtualGroupResponse) GetGlobalVirtualGroup() *GlobalVirtualGroup {
+	if m != nil {
+		return m.GlobalVirtualGroup
+	}
+	return nil
+}
+
+type QueryGlobalVirtualGroupByFamilyIDRequest struct {
+	StorageProviderId          uint32 `protobuf:"varint,1,opt,name=storage_provider_id,json=storageProviderId,proto3" json:"storage_provider_id,omitempty"`
+	GlobalVirtualGroupFamilyId uint32 `protobuf:"varint,2,opt,name=global_virtual_group_family_id,json=globalVirtualGroupFamilyId,proto3" json:"global_virtual_group_family_id,omitempty"`
+}
+
+func (m *QueryGlobalVirtualGroupByFamilyIDRequest) Reset() {
+	*m = QueryGlobalVirtualGroupByFamilyIDRequest{}
+}
+func (m *QueryGlobalVirtualGroupByFamilyIDRequest) String() string { return proto.CompactTextString(m) }
+func (*QueryGlobalVirtualGroupByFamilyIDRequest) ProtoMessage()    {}
+func (*QueryGlobalVirtualGroupByFamilyIDRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_83cd53fc415e00e7, []int{4}
+}
+func (m *QueryGlobalVirtualGroupByFamilyIDRequest) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *QueryGlobalVirtualGroupByFamilyIDRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_QueryGlobalVirtualGroupByFamilyIDRequest.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *QueryGlobalVirtualGroupByFamilyIDRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_QueryGlobalVirtualGroupByFamilyIDRequest.Merge(m, src)
+}
+func (m *QueryGlobalVirtualGroupByFamilyIDRequest) XXX_Size() int {
+	return m.Size()
+}
+func (m *QueryGlobalVirtualGroupByFamilyIDRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_QueryGlobalVirtualGroupByFamilyIDRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryGlobalVirtualGroupByFamilyIDRequest proto.InternalMessageInfo
+
+func (m *QueryGlobalVirtualGroupByFamilyIDRequest) GetStorageProviderId() uint32 {
+	if m != nil {
+		return m.StorageProviderId
+	}
+	return 0
+}
+
+func (m *QueryGlobalVirtualGroupByFamilyIDRequest) GetGlobalVirtualGroupFamilyId() uint32 {
+	if m != nil {
+		return m.GlobalVirtualGroupFamilyId
+	}
+	return 0
+}
+
+type QueryGlobalVirtualGroupByFamilyIDResponse struct {
+	GlobalVirtualGroups []*GlobalVirtualGroup `protobuf:"bytes,1,rep,name=global_virtual_groups,json=globalVirtualGroups,proto3" json:"global_virtual_groups,omitempty"`
+}
+
+func (m *QueryGlobalVirtualGroupByFamilyIDResponse) Reset() {
+	*m = QueryGlobalVirtualGroupByFamilyIDResponse{}
+}
+func (m *QueryGlobalVirtualGroupByFamilyIDResponse) String() string {
+	return proto.CompactTextString(m)
+}
+func (*QueryGlobalVirtualGroupByFamilyIDResponse) ProtoMessage() {}
+func (*QueryGlobalVirtualGroupByFamilyIDResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_83cd53fc415e00e7, []int{5}
+}
+func (m *QueryGlobalVirtualGroupByFamilyIDResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *QueryGlobalVirtualGroupByFamilyIDResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_QueryGlobalVirtualGroupByFamilyIDResponse.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *QueryGlobalVirtualGroupByFamilyIDResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_QueryGlobalVirtualGroupByFamilyIDResponse.Merge(m, src)
+}
+func (m *QueryGlobalVirtualGroupByFamilyIDResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *QueryGlobalVirtualGroupByFamilyIDResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_QueryGlobalVirtualGroupByFamilyIDResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryGlobalVirtualGroupByFamilyIDResponse proto.InternalMessageInfo
+
+func (m *QueryGlobalVirtualGroupByFamilyIDResponse) GetGlobalVirtualGroups() []*GlobalVirtualGroup {
+	if m != nil {
+		return m.GlobalVirtualGroups
+	}
+	return nil
+}
+
+type QueryGlobalVirtualGroupFamiliesRequest struct {
+	StorageProviderId uint32             `protobuf:"varint,1,opt,name=storage_provider_id,json=storageProviderId,proto3" json:"storage_provider_id,omitempty"`
+	Pagination        *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"`
+}
+
+func (m *QueryGlobalVirtualGroupFamiliesRequest) Reset() {
+	*m = QueryGlobalVirtualGroupFamiliesRequest{}
+}
+func (m *QueryGlobalVirtualGroupFamiliesRequest) String() string { return proto.CompactTextString(m) }
+func (*QueryGlobalVirtualGroupFamiliesRequest) ProtoMessage()    {}
+func (*QueryGlobalVirtualGroupFamiliesRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_83cd53fc415e00e7, []int{6}
+}
+func (m *QueryGlobalVirtualGroupFamiliesRequest) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *QueryGlobalVirtualGroupFamiliesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_QueryGlobalVirtualGroupFamiliesRequest.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *QueryGlobalVirtualGroupFamiliesRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_QueryGlobalVirtualGroupFamiliesRequest.Merge(m, src)
+}
+func (m *QueryGlobalVirtualGroupFamiliesRequest) XXX_Size() int {
+	return m.Size()
+}
+func (m *QueryGlobalVirtualGroupFamiliesRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_QueryGlobalVirtualGroupFamiliesRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryGlobalVirtualGroupFamiliesRequest proto.InternalMessageInfo
+
+func (m *QueryGlobalVirtualGroupFamiliesRequest) GetStorageProviderId() uint32 {
+	if m != nil {
+		return m.StorageProviderId
+	}
+	return 0
+}
+
+func (m *QueryGlobalVirtualGroupFamiliesRequest) GetPagination() *query.PageRequest {
+	if m != nil {
+		return m.Pagination
+	}
+	return nil
+}
+
+type QueryGlobalVirtualGroupFamiliesResponse struct {
+	GlobalVirtualGroupFamilies []*GlobalVirtualGroupFamily `protobuf:"bytes,1,rep,name=global_virtual_group_families,json=globalVirtualGroupFamilies,proto3" json:"global_virtual_group_families,omitempty"`
+	Pagination                 *query.PageResponse         `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"`
+}
+
+func (m *QueryGlobalVirtualGroupFamiliesResponse) Reset() {
+	*m = QueryGlobalVirtualGroupFamiliesResponse{}
+}
+func (m *QueryGlobalVirtualGroupFamiliesResponse) String() string { return proto.CompactTextString(m) }
+func (*QueryGlobalVirtualGroupFamiliesResponse) ProtoMessage()    {}
+func (*QueryGlobalVirtualGroupFamiliesResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_83cd53fc415e00e7, []int{7}
+}
+func (m *QueryGlobalVirtualGroupFamiliesResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *QueryGlobalVirtualGroupFamiliesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_QueryGlobalVirtualGroupFamiliesResponse.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *QueryGlobalVirtualGroupFamiliesResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_QueryGlobalVirtualGroupFamiliesResponse.Merge(m, src)
+}
+func (m *QueryGlobalVirtualGroupFamiliesResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *QueryGlobalVirtualGroupFamiliesResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_QueryGlobalVirtualGroupFamiliesResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryGlobalVirtualGroupFamiliesResponse proto.InternalMessageInfo
+
+func (m *QueryGlobalVirtualGroupFamiliesResponse) GetGlobalVirtualGroupFamilies() []*GlobalVirtualGroupFamily {
+	if m != nil {
+		return m.GlobalVirtualGroupFamilies
+	}
+	return nil
+}
+
+func (m *QueryGlobalVirtualGroupFamiliesResponse) GetPagination() *query.PageResponse {
+	if m != nil {
+		return m.Pagination
+	}
+	return nil
+}
+
+type QueryGlobalVirtualGroupFamilyRequest struct {
+	StorageProviderId uint32 `protobuf:"varint,1,opt,name=storage_provider_id,json=storageProviderId,proto3" json:"storage_provider_id,omitempty"`
+	FamilyId          uint32 `protobuf:"varint,2,opt,name=family_id,json=familyId,proto3" json:"family_id,omitempty"`
+}
+
+func (m *QueryGlobalVirtualGroupFamilyRequest) Reset()         { *m = QueryGlobalVirtualGroupFamilyRequest{} }
+func (m *QueryGlobalVirtualGroupFamilyRequest) String() string { return proto.CompactTextString(m) }
+func (*QueryGlobalVirtualGroupFamilyRequest) ProtoMessage()    {}
+func (*QueryGlobalVirtualGroupFamilyRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_83cd53fc415e00e7, []int{8}
+}
+func (m *QueryGlobalVirtualGroupFamilyRequest) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *QueryGlobalVirtualGroupFamilyRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_QueryGlobalVirtualGroupFamilyRequest.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *QueryGlobalVirtualGroupFamilyRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_QueryGlobalVirtualGroupFamilyRequest.Merge(m, src)
+}
+func (m *QueryGlobalVirtualGroupFamilyRequest) XXX_Size() int {
+	return m.Size()
+}
+func (m *QueryGlobalVirtualGroupFamilyRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_QueryGlobalVirtualGroupFamilyRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryGlobalVirtualGroupFamilyRequest proto.InternalMessageInfo
+
+func (m *QueryGlobalVirtualGroupFamilyRequest) GetStorageProviderId() uint32 {
+	if m != nil {
+		return m.StorageProviderId
+	}
+	return 0
+}
+
+func (m *QueryGlobalVirtualGroupFamilyRequest) GetFamilyId() uint32 {
+	if m != nil {
+		return m.FamilyId
+	}
+	return 0
+}
+
+type QueryGlobalVirtualGroupFamilyResponse struct {
+	GlobalVirtualGroupFamily *GlobalVirtualGroupFamily `protobuf:"bytes,1,opt,name=global_virtual_group_family,json=globalVirtualGroupFamily,proto3" json:"global_virtual_group_family,omitempty"`
+}
+
+func (m *QueryGlobalVirtualGroupFamilyResponse) Reset()         { *m = QueryGlobalVirtualGroupFamilyResponse{} }
+func (m *QueryGlobalVirtualGroupFamilyResponse) String() string { return proto.CompactTextString(m) }
+func (*QueryGlobalVirtualGroupFamilyResponse) ProtoMessage()    {}
+func (*QueryGlobalVirtualGroupFamilyResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_83cd53fc415e00e7, []int{9}
+}
+func (m *QueryGlobalVirtualGroupFamilyResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *QueryGlobalVirtualGroupFamilyResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_QueryGlobalVirtualGroupFamilyResponse.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *QueryGlobalVirtualGroupFamilyResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_QueryGlobalVirtualGroupFamilyResponse.Merge(m, src)
+}
+func (m *QueryGlobalVirtualGroupFamilyResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *QueryGlobalVirtualGroupFamilyResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_QueryGlobalVirtualGroupFamilyResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryGlobalVirtualGroupFamilyResponse proto.InternalMessageInfo
+
+func (m *QueryGlobalVirtualGroupFamilyResponse) GetGlobalVirtualGroupFamily() *GlobalVirtualGroupFamily {
+	if m != nil {
+		return m.GlobalVirtualGroupFamily
+	}
+	return nil
+}
+
+func init() {
+	proto.RegisterType((*QueryParamsRequest)(nil), "greenfield.virtualgroup.QueryParamsRequest")
+	proto.RegisterType((*QueryParamsResponse)(nil), "greenfield.virtualgroup.QueryParamsResponse")
+	proto.RegisterType((*QueryGlobalVirtualGroupRequest)(nil), "greenfield.virtualgroup.QueryGlobalVirtualGroupRequest")
+	proto.RegisterType((*QueryGlobalVirtualGroupResponse)(nil), "greenfield.virtualgroup.QueryGlobalVirtualGroupResponse")
+	proto.RegisterType((*QueryGlobalVirtualGroupByFamilyIDRequest)(nil), "greenfield.virtualgroup.QueryGlobalVirtualGroupByFamilyIDRequest")
+	proto.RegisterType((*QueryGlobalVirtualGroupByFamilyIDResponse)(nil), "greenfield.virtualgroup.QueryGlobalVirtualGroupByFamilyIDResponse")
+	proto.RegisterType((*QueryGlobalVirtualGroupFamiliesRequest)(nil), "greenfield.virtualgroup.QueryGlobalVirtualGroupFamiliesRequest")
+	proto.RegisterType((*QueryGlobalVirtualGroupFamiliesResponse)(nil), "greenfield.virtualgroup.QueryGlobalVirtualGroupFamiliesResponse")
+	proto.RegisterType((*QueryGlobalVirtualGroupFamilyRequest)(nil), "greenfield.virtualgroup.QueryGlobalVirtualGroupFamilyRequest")
+	proto.RegisterType((*QueryGlobalVirtualGroupFamilyResponse)(nil), "greenfield.virtualgroup.QueryGlobalVirtualGroupFamilyResponse")
+}
+
+func init() {
+	proto.RegisterFile("greenfield/virtualgroup/query.proto", fileDescriptor_83cd53fc415e00e7)
+}
+
+var fileDescriptor_83cd53fc415e00e7 = []byte{
+	// 726 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0x4f, 0x4f, 0xd4, 0x4e,
+	0x18, 0xde, 0xe1, 0xf7, 0x13, 0xf5, 0x25, 0x1e, 0x1c, 0xd6, 0x40, 0x0a, 0x76, 0xb5, 0x22, 0xe0,
+	0x1f, 0xda, 0x80, 0x80, 0xc4, 0x00, 0xea, 0xc6, 0x40, 0xb8, 0x18, 0xdc, 0x18, 0x4d, 0x4c, 0xcc,
+	0x66, 0xca, 0x0e, 0x65, 0x92, 0xdd, 0x4e, 0x69, 0xbb, 0xc4, 0xde, 0x8c, 0x67, 0x0e, 0x26, 0x9e,
+	0xbc, 0x18, 0x3f, 0x89, 0x67, 0x8e, 0x24, 0x1e, 0xf4, 0xa4, 0x06, 0x3c, 0xe8, 0xa7, 0xd0, 0xec,
+	0x74, 0x36, 0x0b, 0xb4, 0xb3, 0x4b, 0xeb, 0xad, 0xe9, 0xbc, 0x7f, 0x9e, 0xe7, 0x79, 0xe7, 0x7d,
+	0x32, 0x70, 0xcd, 0xf1, 0x29, 0x75, 0x37, 0x19, 0xad, 0xd7, 0xac, 0x1d, 0xe6, 0x87, 0x4d, 0x52,
+	0x77, 0x7c, 0xde, 0xf4, 0xac, 0xed, 0x26, 0xf5, 0x23, 0xd3, 0xf3, 0x79, 0xc8, 0xf1, 0x50, 0x27,
+	0xc8, 0x3c, 0x1a, 0xa4, 0xdd, 0xdc, 0xe0, 0x41, 0x83, 0x07, 0x96, 0x4d, 0x02, 0x1a, 0x67, 0x58,
+	0x3b, 0xd3, 0x36, 0x0d, 0xc9, 0xb4, 0xe5, 0x11, 0x87, 0xb9, 0x24, 0x64, 0xdc, 0x8d, 0x8b, 0x68,
+	0x45, 0x87, 0x3b, 0x5c, 0x7c, 0x5a, 0xad, 0x2f, 0xf9, 0x77, 0xd4, 0xe1, 0xdc, 0xa9, 0x53, 0x8b,
+	0x78, 0xcc, 0x22, 0xae, 0xcb, 0x43, 0x91, 0x12, 0xc8, 0xd3, 0x31, 0x15, 0x3a, 0x8f, 0xf8, 0xa4,
+	0xd1, 0x8e, 0x52, 0x72, 0x08, 0x23, 0x8f, 0xca, 0x20, 0xa3, 0x08, 0xf8, 0x49, 0x0b, 0xe0, 0xba,
+	0xc8, 0xac, 0xd0, 0xed, 0x26, 0x0d, 0x42, 0xe3, 0x29, 0x0c, 0x1e, 0xfb, 0x1b, 0x78, 0xdc, 0x0d,
+	0x28, 0x5e, 0x82, 0xfe, 0xb8, 0xc3, 0x30, 0xba, 0x82, 0x26, 0x07, 0x66, 0x4a, 0xa6, 0x42, 0x01,
+	0x33, 0x4e, 0x2c, 0xff, 0xbf, 0xf7, 0xad, 0x54, 0xa8, 0xc8, 0x24, 0xe3, 0x39, 0xe8, 0xa2, 0xea,
+	0x6a, 0x9d, 0xdb, 0xa4, 0xfe, 0x2c, 0x8e, 0x5f, 0x6d, 0xc5, 0xcb, 0xbe, 0x78, 0x0e, 0x86, 0x1c,
+	0x71, 0x58, 0x95, 0xd5, 0xaa, 0xa2, 0x5c, 0x95, 0xd5, 0x44, 0xc7, 0x0b, 0x95, 0xa2, 0x93, 0xc8,
+	0x5d, 0xab, 0x19, 0xaf, 0x11, 0x94, 0x94, 0x95, 0x25, 0xf6, 0x97, 0x50, 0x4c, 0x2b, 0x2d, 0x99,
+	0xdc, 0x52, 0x32, 0x49, 0x29, 0x89, 0x93, 0x20, 0x8c, 0x0f, 0x08, 0x26, 0x15, 0x10, 0xca, 0xd1,
+	0x0a, 0x69, 0xb0, 0x7a, 0xb4, 0xf6, 0xa8, 0x4d, 0xd3, 0x84, 0xc1, 0x20, 0xe4, 0x3e, 0x71, 0x68,
+	0xd5, 0xf3, 0xf9, 0x0e, 0xab, 0x51, 0xbf, 0x43, 0xf1, 0xa2, 0x3c, 0x5a, 0x97, 0x27, 0x6b, 0x35,
+	0x5c, 0x06, 0x3d, 0x55, 0x96, 0x4d, 0x51, 0xb7, 0x95, 0xda, 0x27, 0x52, 0xb5, 0x24, 0x30, 0xd9,
+	0xba, 0x66, 0xec, 0x22, 0xb8, 0x71, 0x0a, 0x80, 0x52, 0xad, 0x2a, 0x5c, 0x4a, 0xeb, 0xd8, 0x1a,
+	0xfc, 0x7f, 0x59, 0xe5, 0x1a, 0x4c, 0xa2, 0x0a, 0x8c, 0x8f, 0x08, 0xc6, 0x15, 0x70, 0x04, 0x18,
+	0x46, 0x83, 0xbc, 0x6a, 0xad, 0x00, 0x74, 0xb6, 0x4c, 0x28, 0x33, 0x30, 0x33, 0x6e, 0xc6, 0x2b,
+	0x69, 0xb6, 0x56, 0xd2, 0x8c, 0x97, 0x58, 0xae, 0xa4, 0xb9, 0x4e, 0x1c, 0x2a, 0x7b, 0x55, 0x8e,
+	0x64, 0x1a, 0xbf, 0x10, 0x4c, 0xf4, 0x84, 0x28, 0xf5, 0x0a, 0xe1, 0xb2, 0x7a, 0x42, 0x8c, 0xb6,
+	0x75, 0x9b, 0xce, 0xa0, 0x5b, 0x3c, 0x13, 0xe5, 0x4c, 0x19, 0x0d, 0xf0, 0x6a, 0x0a, 0xd3, 0x89,
+	0x9e, 0x4c, 0x63, 0xc8, 0xc7, 0xa8, 0x06, 0x30, 0xd6, 0x8d, 0x69, 0x94, 0x77, 0x14, 0x23, 0x70,
+	0xfe, 0xe4, 0x1d, 0x3d, 0xb7, 0xd9, 0xbe, 0x91, 0xef, 0x11, 0x5c, 0xef, 0xd1, 0x55, 0xaa, 0xeb,
+	0xc1, 0x48, 0x97, 0xfb, 0x2f, 0x57, 0x38, 0x87, 0xb6, 0xc3, 0xaa, 0x7d, 0x99, 0xf9, 0x73, 0x16,
+	0xce, 0x08, 0x6c, 0x78, 0x17, 0x41, 0x7f, 0xec, 0x66, 0x58, 0x7d, 0xeb, 0x93, 0x16, 0xaa, 0xdd,
+	0x3e, 0x5d, 0x70, 0xcc, 0xd0, 0x98, 0x78, 0xf3, 0xf9, 0xe7, 0xbb, 0xbe, 0xab, 0xb8, 0x64, 0x75,
+	0xb7, 0x76, 0xfc, 0x09, 0x01, 0x4e, 0xf2, 0xc1, 0x77, 0xbb, 0x77, 0x53, 0x3a, 0xae, 0xb6, 0x90,
+	0x3d, 0x51, 0x42, 0x9e, 0x13, 0x90, 0x2d, 0x3c, 0xa5, 0x84, 0x9c, 0x36, 0x33, 0xfc, 0x1b, 0xc1,
+	0x68, 0x37, 0x0b, 0xc2, 0x0f, 0xb3, 0x22, 0x4a, 0xf8, 0xab, 0x56, 0xfe, 0x97, 0x12, 0x92, 0x5e,
+	0x59, 0xd0, 0x5b, 0xc4, 0xf7, 0x32, 0xd1, 0xab, 0xda, 0x51, 0xc7, 0x95, 0xf1, 0x77, 0x04, 0x9a,
+	0xda, 0x3c, 0xf0, 0xfd, 0xac, 0x30, 0x4f, 0x38, 0xa3, 0xf6, 0x20, 0x7f, 0x01, 0xc9, 0x72, 0x59,
+	0xb0, 0x5c, 0xc0, 0xf3, 0xd9, 0x58, 0xb6, 0x6d, 0x0d, 0x7f, 0x41, 0x30, 0xac, 0x5a, 0x2f, 0xbc,
+	0x94, 0x0b, 0x5e, 0xdb, 0x6c, 0xb4, 0xe5, 0xbc, 0xe9, 0x92, 0xdb, 0xa2, 0xe0, 0x36, 0x8f, 0x67,
+	0x73, 0x70, 0x8b, 0xca, 0x8f, 0xf7, 0x0e, 0x74, 0xb4, 0x7f, 0xa0, 0xa3, 0x1f, 0x07, 0x3a, 0x7a,
+	0x7b, 0xa8, 0x17, 0xf6, 0x0f, 0xf5, 0xc2, 0xd7, 0x43, 0xbd, 0xf0, 0x62, 0xd6, 0x61, 0xe1, 0x56,
+	0xd3, 0x36, 0x37, 0x78, 0xc3, 0xb2, 0x5d, 0x7b, 0x6a, 0x63, 0x8b, 0x30, 0xf7, 0x68, 0x8f, 0x57,
+	0x29, 0xcf, 0x2d, 0xbb, 0x5f, 0xbc, 0xb7, 0xee, 0xfc, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x02, 0x72,
+	0x7e, 0x9e, 0x5a, 0x0a, 0x00, 0x00,
+}
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConn
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+const _ = grpc.SupportPackageIsVersion4
+
+// QueryClient is the client API for Query service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
+type QueryClient interface {
+	// Parameters queries the parameters of the module.
+	Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error)
+	// Queries a global virtual group by its id.
+	GlobalVirtualGroup(ctx context.Context, in *QueryGlobalVirtualGroupRequest, opts ...grpc.CallOption) (*QueryGlobalVirtualGroupResponse, error)
+	// Queries a list of global virtual groups by family id.
+	GlobalVirtualGroupByFamilyID(ctx context.Context, in *QueryGlobalVirtualGroupByFamilyIDRequest, opts ...grpc.CallOption) (*QueryGlobalVirtualGroupByFamilyIDResponse, error)
+	// Queries a list of global virtual group families by storage provider id.
+	GlobalVirtualGroupFamilies(ctx context.Context, in *QueryGlobalVirtualGroupFamiliesRequest, opts ...grpc.CallOption) (*QueryGlobalVirtualGroupFamiliesResponse, error)
+	// Queries a global virtual group family by its id.
+	GlobalVirtualGroupFamily(ctx context.Context, in *QueryGlobalVirtualGroupFamilyRequest, opts ...grpc.CallOption) (*QueryGlobalVirtualGroupFamilyResponse, error)
+}
+
+type queryClient struct {
+	cc grpc1.ClientConn
+}
+
+func NewQueryClient(cc grpc1.ClientConn) QueryClient {
+	return &queryClient{cc}
+}
+
+func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) {
+	out := new(QueryParamsResponse)
+	err := c.cc.Invoke(ctx, "/greenfield.virtualgroup.Query/Params", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *queryClient) GlobalVirtualGroup(ctx context.Context, in *QueryGlobalVirtualGroupRequest, opts ...grpc.CallOption) (*QueryGlobalVirtualGroupResponse, error) {
+	out := new(QueryGlobalVirtualGroupResponse)
+	err := c.cc.Invoke(ctx, "/greenfield.virtualgroup.Query/GlobalVirtualGroup", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *queryClient) GlobalVirtualGroupByFamilyID(ctx context.Context, in *QueryGlobalVirtualGroupByFamilyIDRequest, opts ...grpc.CallOption) (*QueryGlobalVirtualGroupByFamilyIDResponse, error) {
+	out := new(QueryGlobalVirtualGroupByFamilyIDResponse)
+	err := c.cc.Invoke(ctx, "/greenfield.virtualgroup.Query/GlobalVirtualGroupByFamilyID", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *queryClient) GlobalVirtualGroupFamilies(ctx context.Context, in *QueryGlobalVirtualGroupFamiliesRequest, opts ...grpc.CallOption) (*QueryGlobalVirtualGroupFamiliesResponse, error) {
+	out := new(QueryGlobalVirtualGroupFamiliesResponse)
+	err := c.cc.Invoke(ctx, "/greenfield.virtualgroup.Query/GlobalVirtualGroupFamilies", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *queryClient) GlobalVirtualGroupFamily(ctx context.Context, in *QueryGlobalVirtualGroupFamilyRequest, opts ...grpc.CallOption) (*QueryGlobalVirtualGroupFamilyResponse, error) {
+	out := new(QueryGlobalVirtualGroupFamilyResponse)
+	err := c.cc.Invoke(ctx, "/greenfield.virtualgroup.Query/GlobalVirtualGroupFamily", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+// QueryServer is the server API for Query service.
+type QueryServer interface {
+	// Parameters queries the parameters of the module.
+	Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error)
+	// Queries a global virtual group by its id.
+	GlobalVirtualGroup(context.Context, *QueryGlobalVirtualGroupRequest) (*QueryGlobalVirtualGroupResponse, error)
+	// Queries a list of global virtual groups by family id.
+	GlobalVirtualGroupByFamilyID(context.Context, *QueryGlobalVirtualGroupByFamilyIDRequest) (*QueryGlobalVirtualGroupByFamilyIDResponse, error)
+	// Queries a list of global virtual group families by storage provider id.
+	GlobalVirtualGroupFamilies(context.Context, *QueryGlobalVirtualGroupFamiliesRequest) (*QueryGlobalVirtualGroupFamiliesResponse, error)
+	// Queries a global virtual group family by its id.
+	GlobalVirtualGroupFamily(context.Context, *QueryGlobalVirtualGroupFamilyRequest) (*QueryGlobalVirtualGroupFamilyResponse, error)
+}
+
+// UnimplementedQueryServer can be embedded to have forward compatible implementations.
+type UnimplementedQueryServer struct {
+}
+
+func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Params not implemented")
+}
+func (*UnimplementedQueryServer) GlobalVirtualGroup(ctx context.Context, req *QueryGlobalVirtualGroupRequest) (*QueryGlobalVirtualGroupResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method GlobalVirtualGroup not implemented")
+}
+func (*UnimplementedQueryServer) GlobalVirtualGroupByFamilyID(ctx context.Context, req *QueryGlobalVirtualGroupByFamilyIDRequest) (*QueryGlobalVirtualGroupByFamilyIDResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method GlobalVirtualGroupByFamilyID not implemented")
+}
+func (*UnimplementedQueryServer) GlobalVirtualGroupFamilies(ctx context.Context, req *QueryGlobalVirtualGroupFamiliesRequest) (*QueryGlobalVirtualGroupFamiliesResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method GlobalVirtualGroupFamilies not implemented")
+}
+func (*UnimplementedQueryServer) GlobalVirtualGroupFamily(ctx context.Context, req *QueryGlobalVirtualGroupFamilyRequest) (*QueryGlobalVirtualGroupFamilyResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method GlobalVirtualGroupFamily not implemented")
+}
+
+func RegisterQueryServer(s grpc1.Server, srv QueryServer) {
+	s.RegisterService(&_Query_serviceDesc, srv)
+}
+
+func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(QueryParamsRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(QueryServer).Params(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/greenfield.virtualgroup.Query/Params",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Query_GlobalVirtualGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(QueryGlobalVirtualGroupRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(QueryServer).GlobalVirtualGroup(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/greenfield.virtualgroup.Query/GlobalVirtualGroup",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(QueryServer).GlobalVirtualGroup(ctx, req.(*QueryGlobalVirtualGroupRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Query_GlobalVirtualGroupByFamilyID_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(QueryGlobalVirtualGroupByFamilyIDRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(QueryServer).GlobalVirtualGroupByFamilyID(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/greenfield.virtualgroup.Query/GlobalVirtualGroupByFamilyID",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(QueryServer).GlobalVirtualGroupByFamilyID(ctx, req.(*QueryGlobalVirtualGroupByFamilyIDRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Query_GlobalVirtualGroupFamilies_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(QueryGlobalVirtualGroupFamiliesRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(QueryServer).GlobalVirtualGroupFamilies(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/greenfield.virtualgroup.Query/GlobalVirtualGroupFamilies",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(QueryServer).GlobalVirtualGroupFamilies(ctx, req.(*QueryGlobalVirtualGroupFamiliesRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Query_GlobalVirtualGroupFamily_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(QueryGlobalVirtualGroupFamilyRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(QueryServer).GlobalVirtualGroupFamily(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/greenfield.virtualgroup.Query/GlobalVirtualGroupFamily",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(QueryServer).GlobalVirtualGroupFamily(ctx, req.(*QueryGlobalVirtualGroupFamilyRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+var _Query_serviceDesc = grpc.ServiceDesc{
+	ServiceName: "greenfield.virtualgroup.Query",
+	HandlerType: (*QueryServer)(nil),
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "Params",
+			Handler:    _Query_Params_Handler,
+		},
+		{
+			MethodName: "GlobalVirtualGroup",
+			Handler:    _Query_GlobalVirtualGroup_Handler,
+		},
+		{
+			MethodName: "GlobalVirtualGroupByFamilyID",
+			Handler:    _Query_GlobalVirtualGroupByFamilyID_Handler,
+		},
+		{
+			MethodName: "GlobalVirtualGroupFamilies",
+			Handler:    _Query_GlobalVirtualGroupFamilies_Handler,
+		},
+		{
+			MethodName: "GlobalVirtualGroupFamily",
+			Handler:    _Query_GlobalVirtualGroupFamily_Handler,
+		},
+	},
+	Streams:  []grpc.StreamDesc{},
+	Metadata: "greenfield/virtualgroup/query.proto",
+}
+
+func (m *QueryParamsRequest) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *QueryParamsRequest) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	return len(dAtA) - i, nil
+}
+
+func (m *QueryParamsResponse) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *QueryParamsResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	{
+		size, err := m.Params.MarshalToSizedBuffer(dAtA[:i])
+		if err != nil {
+			return 0, err
+		}
+		i -= size
+		i = encodeVarintQuery(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0xa
+	return len(dAtA) - i, nil
+}
+
+func (m *QueryGlobalVirtualGroupRequest) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *QueryGlobalVirtualGroupRequest) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryGlobalVirtualGroupRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.GlobalVirtualGroupId != 0 {
+		i = encodeVarintQuery(dAtA, i, uint64(m.GlobalVirtualGroupId))
+		i--
+		dAtA[i] = 0x8
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *QueryGlobalVirtualGroupResponse) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *QueryGlobalVirtualGroupResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryGlobalVirtualGroupResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.GlobalVirtualGroup != nil {
+		{
+			size, err := m.GlobalVirtualGroup.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintQuery(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *QueryGlobalVirtualGroupByFamilyIDRequest) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *QueryGlobalVirtualGroupByFamilyIDRequest) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryGlobalVirtualGroupByFamilyIDRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.GlobalVirtualGroupFamilyId != 0 {
+		i = encodeVarintQuery(dAtA, i, uint64(m.GlobalVirtualGroupFamilyId))
+		i--
+		dAtA[i] = 0x10
+	}
+	if m.StorageProviderId != 0 {
+		i = encodeVarintQuery(dAtA, i, uint64(m.StorageProviderId))
+		i--
+		dAtA[i] = 0x8
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *QueryGlobalVirtualGroupByFamilyIDResponse) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *QueryGlobalVirtualGroupByFamilyIDResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryGlobalVirtualGroupByFamilyIDResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.GlobalVirtualGroups) > 0 {
+		for iNdEx := len(m.GlobalVirtualGroups) - 1; iNdEx >= 0; iNdEx-- {
+			{
+				size, err := m.GlobalVirtualGroups[iNdEx].MarshalToSizedBuffer(dAtA[:i])
+				if err != nil {
+					return 0, err
+				}
+				i -= size
+				i = encodeVarintQuery(dAtA, i, uint64(size))
+			}
+			i--
+			dAtA[i] = 0xa
+		}
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *QueryGlobalVirtualGroupFamiliesRequest) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *QueryGlobalVirtualGroupFamiliesRequest) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryGlobalVirtualGroupFamiliesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.Pagination != nil {
+		{
+			size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintQuery(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0x12
+	}
+	if m.StorageProviderId != 0 {
+		i = encodeVarintQuery(dAtA, i, uint64(m.StorageProviderId))
+		i--
+		dAtA[i] = 0x8
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *QueryGlobalVirtualGroupFamiliesResponse) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *QueryGlobalVirtualGroupFamiliesResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryGlobalVirtualGroupFamiliesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.Pagination != nil {
+		{
+			size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintQuery(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0x12
+	}
+	if len(m.GlobalVirtualGroupFamilies) > 0 {
+		for iNdEx := len(m.GlobalVirtualGroupFamilies) - 1; iNdEx >= 0; iNdEx-- {
+			{
+				size, err := m.GlobalVirtualGroupFamilies[iNdEx].MarshalToSizedBuffer(dAtA[:i])
+				if err != nil {
+					return 0, err
+				}
+				i -= size
+				i = encodeVarintQuery(dAtA, i, uint64(size))
+			}
+			i--
+			dAtA[i] = 0xa
+		}
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *QueryGlobalVirtualGroupFamilyRequest) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *QueryGlobalVirtualGroupFamilyRequest) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryGlobalVirtualGroupFamilyRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.FamilyId != 0 {
+		i = encodeVarintQuery(dAtA, i, uint64(m.FamilyId))
+		i--
+		dAtA[i] = 0x10
+	}
+	if m.StorageProviderId != 0 {
+		i = encodeVarintQuery(dAtA, i, uint64(m.StorageProviderId))
+		i--
+		dAtA[i] = 0x8
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *QueryGlobalVirtualGroupFamilyResponse) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *QueryGlobalVirtualGroupFamilyResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryGlobalVirtualGroupFamilyResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.GlobalVirtualGroupFamily != nil {
+		{
+			size, err := m.GlobalVirtualGroupFamily.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintQuery(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func encodeVarintQuery(dAtA []byte, offset int, v uint64) int {
+	offset -= sovQuery(v)
+	base := offset
+	for v >= 1<<7 {
+		dAtA[offset] = uint8(v&0x7f | 0x80)
+		v >>= 7
+		offset++
+	}
+	dAtA[offset] = uint8(v)
+	return base
+}
+func (m *QueryParamsRequest) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	return n
+}
+
+func (m *QueryParamsResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = m.Params.Size()
+	n += 1 + l + sovQuery(uint64(l))
+	return n
+}
+
+func (m *QueryGlobalVirtualGroupRequest) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.GlobalVirtualGroupId != 0 {
+		n += 1 + sovQuery(uint64(m.GlobalVirtualGroupId))
+	}
+	return n
+}
+
+func (m *QueryGlobalVirtualGroupResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.GlobalVirtualGroup != nil {
+		l = m.GlobalVirtualGroup.Size()
+		n += 1 + l + sovQuery(uint64(l))
+	}
+	return n
+}
+
+func (m *QueryGlobalVirtualGroupByFamilyIDRequest) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.StorageProviderId != 0 {
+		n += 1 + sovQuery(uint64(m.StorageProviderId))
+	}
+	if m.GlobalVirtualGroupFamilyId != 0 {
+		n += 1 + sovQuery(uint64(m.GlobalVirtualGroupFamilyId))
+	}
+	return n
+}
+
+func (m *QueryGlobalVirtualGroupByFamilyIDResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if len(m.GlobalVirtualGroups) > 0 {
+		for _, e := range m.GlobalVirtualGroups {
+			l = e.Size()
+			n += 1 + l + sovQuery(uint64(l))
+		}
+	}
+	return n
+}
+
+func (m *QueryGlobalVirtualGroupFamiliesRequest) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.StorageProviderId != 0 {
+		n += 1 + sovQuery(uint64(m.StorageProviderId))
+	}
+	if m.Pagination != nil {
+		l = m.Pagination.Size()
+		n += 1 + l + sovQuery(uint64(l))
+	}
+	return n
+}
+
+func (m *QueryGlobalVirtualGroupFamiliesResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if len(m.GlobalVirtualGroupFamilies) > 0 {
+		for _, e := range m.GlobalVirtualGroupFamilies {
+			l = e.Size()
+			n += 1 + l + sovQuery(uint64(l))
+		}
+	}
+	if m.Pagination != nil {
+		l = m.Pagination.Size()
+		n += 1 + l + sovQuery(uint64(l))
+	}
+	return n
+}
+
+func (m *QueryGlobalVirtualGroupFamilyRequest) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.StorageProviderId != 0 {
+		n += 1 + sovQuery(uint64(m.StorageProviderId))
+	}
+	if m.FamilyId != 0 {
+		n += 1 + sovQuery(uint64(m.FamilyId))
+	}
+	return n
+}
+
+func (m *QueryGlobalVirtualGroupFamilyResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.GlobalVirtualGroupFamily != nil {
+		l = m.GlobalVirtualGroupFamily.Size()
+		n += 1 + l + sovQuery(uint64(l))
+	}
+	return n
+}
+
+func sovQuery(x uint64) (n int) {
+	return (math_bits.Len64(x|1) + 6) / 7
+}
+func sozQuery(x uint64) (n int) {
+	return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowQuery
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: QueryParamsRequest: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: QueryParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		default:
+			iNdEx = preIndex
+			skippy, err := skipQuery(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowQuery
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: QueryParamsResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: QueryParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthQuery
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipQuery(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *QueryGlobalVirtualGroupRequest) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowQuery
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: QueryGlobalVirtualGroupRequest: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: QueryGlobalVirtualGroupRequest: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupId", wireType)
+			}
+			m.GlobalVirtualGroupId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.GlobalVirtualGroupId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipQuery(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *QueryGlobalVirtualGroupResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowQuery
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: QueryGlobalVirtualGroupResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: QueryGlobalVirtualGroupResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroup", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthQuery
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.GlobalVirtualGroup == nil {
+				m.GlobalVirtualGroup = &GlobalVirtualGroup{}
+			}
+			if err := m.GlobalVirtualGroup.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipQuery(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *QueryGlobalVirtualGroupByFamilyIDRequest) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowQuery
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: QueryGlobalVirtualGroupByFamilyIDRequest: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: QueryGlobalVirtualGroupByFamilyIDRequest: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field StorageProviderId", wireType)
+			}
+			m.StorageProviderId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.StorageProviderId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupFamilyId", wireType)
+			}
+			m.GlobalVirtualGroupFamilyId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.GlobalVirtualGroupFamilyId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipQuery(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *QueryGlobalVirtualGroupByFamilyIDResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowQuery
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: QueryGlobalVirtualGroupByFamilyIDResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: QueryGlobalVirtualGroupByFamilyIDResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroups", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthQuery
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.GlobalVirtualGroups = append(m.GlobalVirtualGroups, &GlobalVirtualGroup{})
+			if err := m.GlobalVirtualGroups[len(m.GlobalVirtualGroups)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipQuery(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *QueryGlobalVirtualGroupFamiliesRequest) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowQuery
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: QueryGlobalVirtualGroupFamiliesRequest: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: QueryGlobalVirtualGroupFamiliesRequest: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field StorageProviderId", wireType)
+			}
+			m.StorageProviderId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.StorageProviderId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthQuery
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.Pagination == nil {
+				m.Pagination = &query.PageRequest{}
+			}
+			if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipQuery(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *QueryGlobalVirtualGroupFamiliesResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowQuery
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: QueryGlobalVirtualGroupFamiliesResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: QueryGlobalVirtualGroupFamiliesResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupFamilies", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthQuery
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.GlobalVirtualGroupFamilies = append(m.GlobalVirtualGroupFamilies, &GlobalVirtualGroupFamily{})
+			if err := m.GlobalVirtualGroupFamilies[len(m.GlobalVirtualGroupFamilies)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthQuery
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.Pagination == nil {
+				m.Pagination = &query.PageResponse{}
+			}
+			if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipQuery(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *QueryGlobalVirtualGroupFamilyRequest) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowQuery
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: QueryGlobalVirtualGroupFamilyRequest: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: QueryGlobalVirtualGroupFamilyRequest: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field StorageProviderId", wireType)
+			}
+			m.StorageProviderId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.StorageProviderId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field FamilyId", wireType)
+			}
+			m.FamilyId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.FamilyId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipQuery(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *QueryGlobalVirtualGroupFamilyResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowQuery
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: QueryGlobalVirtualGroupFamilyResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: QueryGlobalVirtualGroupFamilyResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupFamily", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthQuery
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.GlobalVirtualGroupFamily == nil {
+				m.GlobalVirtualGroupFamily = &GlobalVirtualGroupFamily{}
+			}
+			if err := m.GlobalVirtualGroupFamily.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipQuery(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func skipQuery(dAtA []byte) (n int, err error) {
+	l := len(dAtA)
+	iNdEx := 0
+	depth := 0
+	for iNdEx < l {
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return 0, ErrIntOverflowQuery
+			}
+			if iNdEx >= l {
+				return 0, io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		wireType := int(wire & 0x7)
+		switch wireType {
+		case 0:
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				iNdEx++
+				if dAtA[iNdEx-1] < 0x80 {
+					break
+				}
+			}
+		case 1:
+			iNdEx += 8
+		case 2:
+			var length int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				length |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if length < 0 {
+				return 0, ErrInvalidLengthQuery
+			}
+			iNdEx += length
+		case 3:
+			depth++
+		case 4:
+			if depth == 0 {
+				return 0, ErrUnexpectedEndOfGroupQuery
+			}
+			depth--
+		case 5:
+			iNdEx += 4
+		default:
+			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+		}
+		if iNdEx < 0 {
+			return 0, ErrInvalidLengthQuery
+		}
+		if depth == 0 {
+			return iNdEx, nil
+		}
+	}
+	return 0, io.ErrUnexpectedEOF
+}
+
+var (
+	ErrInvalidLengthQuery        = fmt.Errorf("proto: negative length found during unmarshaling")
+	ErrIntOverflowQuery          = fmt.Errorf("proto: integer overflow")
+	ErrUnexpectedEndOfGroupQuery = fmt.Errorf("proto: unexpected end of group")
+)
diff --git a/x/virtualgroup/types/query.pb.gw.go b/x/virtualgroup/types/query.pb.gw.go
new file mode 100644
index 000000000..fd72384b0
--- /dev/null
+++ b/x/virtualgroup/types/query.pb.gw.go
@@ -0,0 +1,485 @@
+// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
+// source: greenfield/virtualgroup/query.proto
+
+/*
+Package types is a reverse proxy.
+
+It translates gRPC into RESTful JSON APIs.
+*/
+package types
+
+import (
+	"context"
+	"io"
+	"net/http"
+
+	"github.com/golang/protobuf/descriptor"
+	"github.com/golang/protobuf/proto"
+	"github.com/grpc-ecosystem/grpc-gateway/runtime"
+	"github.com/grpc-ecosystem/grpc-gateway/utilities"
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/grpclog"
+	"google.golang.org/grpc/metadata"
+	"google.golang.org/grpc/status"
+)
+
+// Suppress "imported and not used" errors
+var _ codes.Code
+var _ io.Reader
+var _ status.Status
+var _ = runtime.String
+var _ = utilities.NewDoubleArray
+var _ = descriptor.ForMessage
+var _ = metadata.Join
+
+func request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryParamsRequest
+	var metadata runtime.ServerMetadata
+
+	msg, err := client.Params(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryParamsRequest
+	var metadata runtime.ServerMetadata
+
+	msg, err := server.Params(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
+var (
+	filter_Query_GlobalVirtualGroup_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
+)
+
+func request_Query_GlobalVirtualGroup_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryGlobalVirtualGroupRequest
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_GlobalVirtualGroup_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := client.GlobalVirtualGroup(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_Query_GlobalVirtualGroup_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryGlobalVirtualGroupRequest
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_GlobalVirtualGroup_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := server.GlobalVirtualGroup(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
+var (
+	filter_Query_GlobalVirtualGroupByFamilyID_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
+)
+
+func request_Query_GlobalVirtualGroupByFamilyID_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryGlobalVirtualGroupByFamilyIDRequest
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_GlobalVirtualGroupByFamilyID_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := client.GlobalVirtualGroupByFamilyID(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_Query_GlobalVirtualGroupByFamilyID_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryGlobalVirtualGroupByFamilyIDRequest
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_GlobalVirtualGroupByFamilyID_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := server.GlobalVirtualGroupByFamilyID(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
+var (
+	filter_Query_GlobalVirtualGroupFamilies_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
+)
+
+func request_Query_GlobalVirtualGroupFamilies_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryGlobalVirtualGroupFamiliesRequest
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_GlobalVirtualGroupFamilies_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := client.GlobalVirtualGroupFamilies(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_Query_GlobalVirtualGroupFamilies_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryGlobalVirtualGroupFamiliesRequest
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_GlobalVirtualGroupFamilies_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := server.GlobalVirtualGroupFamilies(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
+var (
+	filter_Query_GlobalVirtualGroupFamily_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
+)
+
+func request_Query_GlobalVirtualGroupFamily_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryGlobalVirtualGroupFamilyRequest
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_GlobalVirtualGroupFamily_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := client.GlobalVirtualGroupFamily(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_Query_GlobalVirtualGroupFamily_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryGlobalVirtualGroupFamilyRequest
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_GlobalVirtualGroupFamily_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := server.GlobalVirtualGroupFamily(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
+// RegisterQueryHandlerServer registers the http handlers for service Query to "mux".
+// UnaryRPC     :call QueryServer directly.
+// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
+// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterQueryHandlerFromEndpoint instead.
+func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error {
+
+	mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		var stream runtime.ServerTransportStream
+		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_Query_Params_0(rctx, inboundMarshaler, server, req, pathParams)
+		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("GET", pattern_Query_GlobalVirtualGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		var stream runtime.ServerTransportStream
+		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_Query_GlobalVirtualGroup_0(rctx, inboundMarshaler, server, req, pathParams)
+		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_Query_GlobalVirtualGroup_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("GET", pattern_Query_GlobalVirtualGroupByFamilyID_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		var stream runtime.ServerTransportStream
+		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_Query_GlobalVirtualGroupByFamilyID_0(rctx, inboundMarshaler, server, req, pathParams)
+		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_Query_GlobalVirtualGroupByFamilyID_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("GET", pattern_Query_GlobalVirtualGroupFamilies_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		var stream runtime.ServerTransportStream
+		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_Query_GlobalVirtualGroupFamilies_0(rctx, inboundMarshaler, server, req, pathParams)
+		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_Query_GlobalVirtualGroupFamilies_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("GET", pattern_Query_GlobalVirtualGroupFamily_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		var stream runtime.ServerTransportStream
+		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_Query_GlobalVirtualGroupFamily_0(rctx, inboundMarshaler, server, req, pathParams)
+		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_Query_GlobalVirtualGroupFamily_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	return nil
+}
+
+// RegisterQueryHandlerFromEndpoint is same as RegisterQueryHandler but
+// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
+func RegisterQueryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
+	conn, err := grpc.Dial(endpoint, opts...)
+	if err != nil {
+		return err
+	}
+	defer func() {
+		if err != nil {
+			if cerr := conn.Close(); cerr != nil {
+				grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
+			}
+			return
+		}
+		go func() {
+			<-ctx.Done()
+			if cerr := conn.Close(); cerr != nil {
+				grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
+			}
+		}()
+	}()
+
+	return RegisterQueryHandler(ctx, mux, conn)
+}
+
+// RegisterQueryHandler registers the http handlers for service Query to "mux".
+// The handlers forward requests to the grpc endpoint over "conn".
+func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
+	return RegisterQueryHandlerClient(ctx, mux, NewQueryClient(conn))
+}
+
+// RegisterQueryHandlerClient registers the http handlers for service Query
+// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "QueryClient".
+// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "QueryClient"
+// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
+// "QueryClient" to call the correct interceptors.
+func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error {
+
+	mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_Query_Params_0(rctx, inboundMarshaler, client, req, pathParams)
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("GET", pattern_Query_GlobalVirtualGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_Query_GlobalVirtualGroup_0(rctx, inboundMarshaler, client, req, pathParams)
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_Query_GlobalVirtualGroup_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("GET", pattern_Query_GlobalVirtualGroupByFamilyID_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_Query_GlobalVirtualGroupByFamilyID_0(rctx, inboundMarshaler, client, req, pathParams)
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_Query_GlobalVirtualGroupByFamilyID_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("GET", pattern_Query_GlobalVirtualGroupFamilies_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_Query_GlobalVirtualGroupFamilies_0(rctx, inboundMarshaler, client, req, pathParams)
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_Query_GlobalVirtualGroupFamilies_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("GET", pattern_Query_GlobalVirtualGroupFamily_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_Query_GlobalVirtualGroupFamily_0(rctx, inboundMarshaler, client, req, pathParams)
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_Query_GlobalVirtualGroupFamily_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	return nil
+}
+
+var (
+	pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"greenfield", "virtualgroup", "params"}, "", runtime.AssumeColonVerbOpt(false)))
+
+	pattern_Query_GlobalVirtualGroup_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"greenfield", "virtualgroup", "global_virtual_group"}, "", runtime.AssumeColonVerbOpt(false)))
+
+	pattern_Query_GlobalVirtualGroupByFamilyID_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"greenfield", "virtualgroup", "global_virtual_group_by_family_id"}, "", runtime.AssumeColonVerbOpt(false)))
+
+	pattern_Query_GlobalVirtualGroupFamilies_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"greenfield", "virtualgroup", "global_virtual_group_families"}, "", runtime.AssumeColonVerbOpt(false)))
+
+	pattern_Query_GlobalVirtualGroupFamily_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"greenfield", "virtualgroup", "global_virtual_group_family"}, "", runtime.AssumeColonVerbOpt(false)))
+)
+
+var (
+	forward_Query_Params_0 = runtime.ForwardResponseMessage
+
+	forward_Query_GlobalVirtualGroup_0 = runtime.ForwardResponseMessage
+
+	forward_Query_GlobalVirtualGroupByFamilyID_0 = runtime.ForwardResponseMessage
+
+	forward_Query_GlobalVirtualGroupFamilies_0 = runtime.ForwardResponseMessage
+
+	forward_Query_GlobalVirtualGroupFamily_0 = runtime.ForwardResponseMessage
+)
diff --git a/x/virtualgroup/types/tx.pb.go b/x/virtualgroup/types/tx.pb.go
new file mode 100644
index 000000000..51533919e
--- /dev/null
+++ b/x/virtualgroup/types/tx.pb.go
@@ -0,0 +1,5012 @@
+// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// source: greenfield/virtualgroup/tx.proto
+
+package types
+
+import (
+	context "context"
+	fmt "fmt"
+	common "github.com/bnb-chain/greenfield/types/common"
+	_ "github.com/cosmos/cosmos-proto"
+	types "github.com/cosmos/cosmos-sdk/types"
+	_ "github.com/cosmos/cosmos-sdk/types/msgservice"
+	_ "github.com/cosmos/gogoproto/gogoproto"
+	grpc1 "github.com/cosmos/gogoproto/grpc"
+	proto "github.com/cosmos/gogoproto/proto"
+	grpc "google.golang.org/grpc"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+	io "io"
+	math "math"
+	math_bits "math/bits"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
+
+// MsgUpdateParams is the Msg/UpdateParams request type.
+type MsgUpdateParams struct {
+	// authority is the address that controls the module (defaults to x/gov unless overwritten).
+	Authority string `protobuf:"bytes,1,opt,name=authority,proto3" json:"authority,omitempty"`
+	// params defines the x/virtualgroup parameters to update.
+	// NOTE: All parameters must be supplied.
+	Params Params `protobuf:"bytes,2,opt,name=params,proto3" json:"params"`
+}
+
+func (m *MsgUpdateParams) Reset()         { *m = MsgUpdateParams{} }
+func (m *MsgUpdateParams) String() string { return proto.CompactTextString(m) }
+func (*MsgUpdateParams) ProtoMessage()    {}
+func (*MsgUpdateParams) Descriptor() ([]byte, []int) {
+	return fileDescriptor_478f7001009bf3f2, []int{0}
+}
+func (m *MsgUpdateParams) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgUpdateParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgUpdateParams.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MsgUpdateParams) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgUpdateParams.Merge(m, src)
+}
+func (m *MsgUpdateParams) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgUpdateParams) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgUpdateParams.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgUpdateParams proto.InternalMessageInfo
+
+func (m *MsgUpdateParams) GetAuthority() string {
+	if m != nil {
+		return m.Authority
+	}
+	return ""
+}
+
+func (m *MsgUpdateParams) GetParams() Params {
+	if m != nil {
+		return m.Params
+	}
+	return Params{}
+}
+
+// MsgUpdateParamsResponse defines the response structure for executing a
+// MsgUpdateParams message.
+type MsgUpdateParamsResponse struct {
+}
+
+func (m *MsgUpdateParamsResponse) Reset()         { *m = MsgUpdateParamsResponse{} }
+func (m *MsgUpdateParamsResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgUpdateParamsResponse) ProtoMessage()    {}
+func (*MsgUpdateParamsResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_478f7001009bf3f2, []int{1}
+}
+func (m *MsgUpdateParamsResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgUpdateParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgUpdateParamsResponse.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MsgUpdateParamsResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgUpdateParamsResponse.Merge(m, src)
+}
+func (m *MsgUpdateParamsResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgUpdateParamsResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgUpdateParamsResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgUpdateParamsResponse proto.InternalMessageInfo
+
+type MsgCreateGlobalVirtualGroup struct {
+	// storage_provider defines the operator account address of the storage provider who create the global virtual group.
+	StorageProvider string `protobuf:"bytes,1,opt,name=storage_provider,json=storageProvider,proto3" json:"storage_provider,omitempty"`
+	// family_id is the identifier for the virtual group's family.
+	FamilyId uint32 `protobuf:"varint,2,opt,name=family_id,json=familyId,proto3" json:"family_id,omitempty"`
+	// secondary_sp_id is a list of secondary storage provider IDs associated with the virtual group.
+	SecondarySpIds []uint32 `protobuf:"varint,3,rep,packed,name=secondary_sp_ids,json=secondarySpIds,proto3" json:"secondary_sp_ids,omitempty"`
+	// total_deposit is the total deposit amount required for the virtual group.
+	// The tokens needs deposited and the size of storage are correlated.
+	Deposit types.Coin `protobuf:"bytes,4,opt,name=deposit,proto3" json:"deposit"`
+}
+
+func (m *MsgCreateGlobalVirtualGroup) Reset()         { *m = MsgCreateGlobalVirtualGroup{} }
+func (m *MsgCreateGlobalVirtualGroup) String() string { return proto.CompactTextString(m) }
+func (*MsgCreateGlobalVirtualGroup) ProtoMessage()    {}
+func (*MsgCreateGlobalVirtualGroup) Descriptor() ([]byte, []int) {
+	return fileDescriptor_478f7001009bf3f2, []int{2}
+}
+func (m *MsgCreateGlobalVirtualGroup) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgCreateGlobalVirtualGroup) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgCreateGlobalVirtualGroup.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MsgCreateGlobalVirtualGroup) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgCreateGlobalVirtualGroup.Merge(m, src)
+}
+func (m *MsgCreateGlobalVirtualGroup) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgCreateGlobalVirtualGroup) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgCreateGlobalVirtualGroup.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgCreateGlobalVirtualGroup proto.InternalMessageInfo
+
+func (m *MsgCreateGlobalVirtualGroup) GetStorageProvider() string {
+	if m != nil {
+		return m.StorageProvider
+	}
+	return ""
+}
+
+func (m *MsgCreateGlobalVirtualGroup) GetFamilyId() uint32 {
+	if m != nil {
+		return m.FamilyId
+	}
+	return 0
+}
+
+func (m *MsgCreateGlobalVirtualGroup) GetSecondarySpIds() []uint32 {
+	if m != nil {
+		return m.SecondarySpIds
+	}
+	return nil
+}
+
+func (m *MsgCreateGlobalVirtualGroup) GetDeposit() types.Coin {
+	if m != nil {
+		return m.Deposit
+	}
+	return types.Coin{}
+}
+
+type MsgCreateGlobalVirtualGroupResponse struct {
+}
+
+func (m *MsgCreateGlobalVirtualGroupResponse) Reset()         { *m = MsgCreateGlobalVirtualGroupResponse{} }
+func (m *MsgCreateGlobalVirtualGroupResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgCreateGlobalVirtualGroupResponse) ProtoMessage()    {}
+func (*MsgCreateGlobalVirtualGroupResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_478f7001009bf3f2, []int{3}
+}
+func (m *MsgCreateGlobalVirtualGroupResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgCreateGlobalVirtualGroupResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgCreateGlobalVirtualGroupResponse.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MsgCreateGlobalVirtualGroupResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgCreateGlobalVirtualGroupResponse.Merge(m, src)
+}
+func (m *MsgCreateGlobalVirtualGroupResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgCreateGlobalVirtualGroupResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgCreateGlobalVirtualGroupResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgCreateGlobalVirtualGroupResponse proto.InternalMessageInfo
+
+type MsgDeleteGlobalVirtualGroup struct {
+	// storage_provider defines the operator account address of the storage provider who delete the global virtual group.
+	StorageProvider string `protobuf:"bytes,1,opt,name=storage_provider,json=storageProvider,proto3" json:"storage_provider,omitempty"`
+	// global_virtual_group_id is the identifier of the global virtual group.
+	GlobalVirtualGroupId uint32 `protobuf:"varint,2,opt,name=global_virtual_group_id,json=globalVirtualGroupId,proto3" json:"global_virtual_group_id,omitempty"`
+}
+
+func (m *MsgDeleteGlobalVirtualGroup) Reset()         { *m = MsgDeleteGlobalVirtualGroup{} }
+func (m *MsgDeleteGlobalVirtualGroup) String() string { return proto.CompactTextString(m) }
+func (*MsgDeleteGlobalVirtualGroup) ProtoMessage()    {}
+func (*MsgDeleteGlobalVirtualGroup) Descriptor() ([]byte, []int) {
+	return fileDescriptor_478f7001009bf3f2, []int{4}
+}
+func (m *MsgDeleteGlobalVirtualGroup) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgDeleteGlobalVirtualGroup) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgDeleteGlobalVirtualGroup.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MsgDeleteGlobalVirtualGroup) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgDeleteGlobalVirtualGroup.Merge(m, src)
+}
+func (m *MsgDeleteGlobalVirtualGroup) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgDeleteGlobalVirtualGroup) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgDeleteGlobalVirtualGroup.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgDeleteGlobalVirtualGroup proto.InternalMessageInfo
+
+func (m *MsgDeleteGlobalVirtualGroup) GetStorageProvider() string {
+	if m != nil {
+		return m.StorageProvider
+	}
+	return ""
+}
+
+func (m *MsgDeleteGlobalVirtualGroup) GetGlobalVirtualGroupId() uint32 {
+	if m != nil {
+		return m.GlobalVirtualGroupId
+	}
+	return 0
+}
+
+type MsgDeleteGlobalVirtualGroupResponse struct {
+}
+
+func (m *MsgDeleteGlobalVirtualGroupResponse) Reset()         { *m = MsgDeleteGlobalVirtualGroupResponse{} }
+func (m *MsgDeleteGlobalVirtualGroupResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgDeleteGlobalVirtualGroupResponse) ProtoMessage()    {}
+func (*MsgDeleteGlobalVirtualGroupResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_478f7001009bf3f2, []int{5}
+}
+func (m *MsgDeleteGlobalVirtualGroupResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgDeleteGlobalVirtualGroupResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgDeleteGlobalVirtualGroupResponse.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MsgDeleteGlobalVirtualGroupResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgDeleteGlobalVirtualGroupResponse.Merge(m, src)
+}
+func (m *MsgDeleteGlobalVirtualGroupResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgDeleteGlobalVirtualGroupResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgDeleteGlobalVirtualGroupResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgDeleteGlobalVirtualGroupResponse proto.InternalMessageInfo
+
+type MsgDeposit struct {
+	// storage_provider defines the operator/funding account address of the storage provider who deposit to the global virtual group.
+	StorageProvider string `protobuf:"bytes,1,opt,name=storage_provider,json=storageProvider,proto3" json:"storage_provider,omitempty"`
+	// global_virtual_group_id is the identifier of the global virtual group.
+	GlobalVirtualGroupId uint32 `protobuf:"varint,2,opt,name=global_virtual_group_id,json=globalVirtualGroupId,proto3" json:"global_virtual_group_id,omitempty"`
+	// deposit is the amount of tokens being deposited for the global virtual group.
+	Deposit types.Coin `protobuf:"bytes,3,opt,name=deposit,proto3" json:"deposit"`
+}
+
+func (m *MsgDeposit) Reset()         { *m = MsgDeposit{} }
+func (m *MsgDeposit) String() string { return proto.CompactTextString(m) }
+func (*MsgDeposit) ProtoMessage()    {}
+func (*MsgDeposit) Descriptor() ([]byte, []int) {
+	return fileDescriptor_478f7001009bf3f2, []int{6}
+}
+func (m *MsgDeposit) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgDeposit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgDeposit.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MsgDeposit) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgDeposit.Merge(m, src)
+}
+func (m *MsgDeposit) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgDeposit) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgDeposit.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgDeposit proto.InternalMessageInfo
+
+func (m *MsgDeposit) GetStorageProvider() string {
+	if m != nil {
+		return m.StorageProvider
+	}
+	return ""
+}
+
+func (m *MsgDeposit) GetGlobalVirtualGroupId() uint32 {
+	if m != nil {
+		return m.GlobalVirtualGroupId
+	}
+	return 0
+}
+
+func (m *MsgDeposit) GetDeposit() types.Coin {
+	if m != nil {
+		return m.Deposit
+	}
+	return types.Coin{}
+}
+
+type MsgDepositResponse struct {
+}
+
+func (m *MsgDepositResponse) Reset()         { *m = MsgDepositResponse{} }
+func (m *MsgDepositResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgDepositResponse) ProtoMessage()    {}
+func (*MsgDepositResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_478f7001009bf3f2, []int{7}
+}
+func (m *MsgDepositResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgDepositResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgDepositResponse.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MsgDepositResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgDepositResponse.Merge(m, src)
+}
+func (m *MsgDepositResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgDepositResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgDepositResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgDepositResponse proto.InternalMessageInfo
+
+type MsgWithdraw struct {
+	// storage_provider defines the operator/funding account address of the storage provider who withdraw from the global virtual group.
+	StorageProvider string `protobuf:"bytes,1,opt,name=storage_provider,json=storageProvider,proto3" json:"storage_provider,omitempty"`
+	// global_virtual_group_id is the identifier of the global virtual group.
+	GlobalVirtualGroupId uint32 `protobuf:"varint,2,opt,name=global_virtual_group_id,json=globalVirtualGroupId,proto3" json:"global_virtual_group_id,omitempty"`
+	// withdraw is the amount of coins to be withdrawn.
+	// The amount needs to be smaller than stored_size * storage_staking_price
+	Withdraw types.Coin `protobuf:"bytes,3,opt,name=withdraw,proto3" json:"withdraw"`
+}
+
+func (m *MsgWithdraw) Reset()         { *m = MsgWithdraw{} }
+func (m *MsgWithdraw) String() string { return proto.CompactTextString(m) }
+func (*MsgWithdraw) ProtoMessage()    {}
+func (*MsgWithdraw) Descriptor() ([]byte, []int) {
+	return fileDescriptor_478f7001009bf3f2, []int{8}
+}
+func (m *MsgWithdraw) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgWithdraw) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgWithdraw.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MsgWithdraw) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgWithdraw.Merge(m, src)
+}
+func (m *MsgWithdraw) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgWithdraw) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgWithdraw.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgWithdraw proto.InternalMessageInfo
+
+func (m *MsgWithdraw) GetStorageProvider() string {
+	if m != nil {
+		return m.StorageProvider
+	}
+	return ""
+}
+
+func (m *MsgWithdraw) GetGlobalVirtualGroupId() uint32 {
+	if m != nil {
+		return m.GlobalVirtualGroupId
+	}
+	return 0
+}
+
+func (m *MsgWithdraw) GetWithdraw() types.Coin {
+	if m != nil {
+		return m.Withdraw
+	}
+	return types.Coin{}
+}
+
+type MsgWithdrawResponse struct {
+}
+
+func (m *MsgWithdrawResponse) Reset()         { *m = MsgWithdrawResponse{} }
+func (m *MsgWithdrawResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgWithdrawResponse) ProtoMessage()    {}
+func (*MsgWithdrawResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_478f7001009bf3f2, []int{9}
+}
+func (m *MsgWithdrawResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgWithdrawResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgWithdrawResponse.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MsgWithdrawResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgWithdrawResponse.Merge(m, src)
+}
+func (m *MsgWithdrawResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgWithdrawResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgWithdrawResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgWithdrawResponse proto.InternalMessageInfo
+
+type MsgSwapOut struct {
+	// storage_provider defines the operator account address of the storage provider who want to swap out from the global virtual group.
+	StorageProvider string `protobuf:"bytes,1,opt,name=storage_provider,json=storageProvider,proto3" json:"storage_provider,omitempty"`
+	// virtual_group_family_id is the identifier of the virtual group family.
+	// if it set to non-zero, it represents that the operator swap out as the primary storage provider
+	// it it set to zero, it represents that the operator swap out as the secondary storage provider.
+	GlobalVirtualGroupFamilyId uint32 `protobuf:"varint,2,opt,name=global_virtual_group_family_id,json=globalVirtualGroupFamilyId,proto3" json:"global_virtual_group_family_id,omitempty"`
+	// global_virtual_group_ids is a list of global virtual group IDs associated with the swap out.
+	// It allows to be empty only when the operator is the primary storage provider.
+	GlobalVirtualGroupIds []uint32 `protobuf:"varint,3,rep,packed,name=global_virtual_group_ids,json=globalVirtualGroupIds,proto3" json:"global_virtual_group_ids,omitempty"`
+	// successor_sp_id is the unique id of the successor storage provider.
+	SuccessorSpId uint32 `protobuf:"varint,4,opt,name=successor_sp_id,json=successorSpId,proto3" json:"successor_sp_id,omitempty"`
+	// approval includes an expiration time and a signature.
+	// The fields to be signed with contains the necessary information of the successor.
+	SuccessorSpApproval *common.Approval `protobuf:"bytes,5,opt,name=successor_sp_approval,json=successorSpApproval,proto3" json:"successor_sp_approval,omitempty"`
+}
+
+func (m *MsgSwapOut) Reset()         { *m = MsgSwapOut{} }
+func (m *MsgSwapOut) String() string { return proto.CompactTextString(m) }
+func (*MsgSwapOut) ProtoMessage()    {}
+func (*MsgSwapOut) Descriptor() ([]byte, []int) {
+	return fileDescriptor_478f7001009bf3f2, []int{10}
+}
+func (m *MsgSwapOut) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgSwapOut) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgSwapOut.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MsgSwapOut) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgSwapOut.Merge(m, src)
+}
+func (m *MsgSwapOut) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgSwapOut) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgSwapOut.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgSwapOut proto.InternalMessageInfo
+
+func (m *MsgSwapOut) GetStorageProvider() string {
+	if m != nil {
+		return m.StorageProvider
+	}
+	return ""
+}
+
+func (m *MsgSwapOut) GetGlobalVirtualGroupFamilyId() uint32 {
+	if m != nil {
+		return m.GlobalVirtualGroupFamilyId
+	}
+	return 0
+}
+
+func (m *MsgSwapOut) GetGlobalVirtualGroupIds() []uint32 {
+	if m != nil {
+		return m.GlobalVirtualGroupIds
+	}
+	return nil
+}
+
+func (m *MsgSwapOut) GetSuccessorSpId() uint32 {
+	if m != nil {
+		return m.SuccessorSpId
+	}
+	return 0
+}
+
+func (m *MsgSwapOut) GetSuccessorSpApproval() *common.Approval {
+	if m != nil {
+		return m.SuccessorSpApproval
+	}
+	return nil
+}
+
+type MsgSwapOutResponse struct {
+}
+
+func (m *MsgSwapOutResponse) Reset()         { *m = MsgSwapOutResponse{} }
+func (m *MsgSwapOutResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgSwapOutResponse) ProtoMessage()    {}
+func (*MsgSwapOutResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_478f7001009bf3f2, []int{11}
+}
+func (m *MsgSwapOutResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgSwapOutResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgSwapOutResponse.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MsgSwapOutResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgSwapOutResponse.Merge(m, src)
+}
+func (m *MsgSwapOutResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgSwapOutResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgSwapOutResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgSwapOutResponse proto.InternalMessageInfo
+
+type MsgCompleteSwapOut struct {
+	// storage_provider defines the operator account address of the storage provider who complete swap out task.
+	StorageProvider string `protobuf:"bytes,1,opt,name=storage_provider,json=storageProvider,proto3" json:"storage_provider,omitempty"`
+	// virtual_group_family_id is the identifier of the virtual group family.
+	// if it set to non-zero, it represents that the operator swap out as the primary storage provider
+	// it it set to zero, it represents that the operator swap out as the secondary storage provider.
+	GlobalVirtualGroupFamilyId uint32 `protobuf:"varint,2,opt,name=global_virtual_group_family_id,json=globalVirtualGroupFamilyId,proto3" json:"global_virtual_group_family_id,omitempty"`
+	// global_virtual_group_ids is a list of global virtual group IDs associated with the swap out.
+	// It allows to be empty only when the operator is the primary storage provider.
+	GlobalVirtualGroupIds []uint32 `protobuf:"varint,3,rep,packed,name=global_virtual_group_ids,json=globalVirtualGroupIds,proto3" json:"global_virtual_group_ids,omitempty"`
+}
+
+func (m *MsgCompleteSwapOut) Reset()         { *m = MsgCompleteSwapOut{} }
+func (m *MsgCompleteSwapOut) String() string { return proto.CompactTextString(m) }
+func (*MsgCompleteSwapOut) ProtoMessage()    {}
+func (*MsgCompleteSwapOut) Descriptor() ([]byte, []int) {
+	return fileDescriptor_478f7001009bf3f2, []int{12}
+}
+func (m *MsgCompleteSwapOut) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgCompleteSwapOut) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgCompleteSwapOut.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MsgCompleteSwapOut) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgCompleteSwapOut.Merge(m, src)
+}
+func (m *MsgCompleteSwapOut) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgCompleteSwapOut) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgCompleteSwapOut.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgCompleteSwapOut proto.InternalMessageInfo
+
+func (m *MsgCompleteSwapOut) GetStorageProvider() string {
+	if m != nil {
+		return m.StorageProvider
+	}
+	return ""
+}
+
+func (m *MsgCompleteSwapOut) GetGlobalVirtualGroupFamilyId() uint32 {
+	if m != nil {
+		return m.GlobalVirtualGroupFamilyId
+	}
+	return 0
+}
+
+func (m *MsgCompleteSwapOut) GetGlobalVirtualGroupIds() []uint32 {
+	if m != nil {
+		return m.GlobalVirtualGroupIds
+	}
+	return nil
+}
+
+type MsgCompleteSwapOutResponse struct {
+}
+
+func (m *MsgCompleteSwapOutResponse) Reset()         { *m = MsgCompleteSwapOutResponse{} }
+func (m *MsgCompleteSwapOutResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgCompleteSwapOutResponse) ProtoMessage()    {}
+func (*MsgCompleteSwapOutResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_478f7001009bf3f2, []int{13}
+}
+func (m *MsgCompleteSwapOutResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgCompleteSwapOutResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgCompleteSwapOutResponse.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MsgCompleteSwapOutResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgCompleteSwapOutResponse.Merge(m, src)
+}
+func (m *MsgCompleteSwapOutResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgCompleteSwapOutResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgCompleteSwapOutResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgCompleteSwapOutResponse proto.InternalMessageInfo
+
+type MsgCancelSwapOut struct {
+	// storage_provider defines the operator account address of the storage provider who cancel the swap out task.
+	StorageProvider string `protobuf:"bytes,1,opt,name=storage_provider,json=storageProvider,proto3" json:"storage_provider,omitempty"`
+	// virtual_group_family_id is the identifier of the virtual group family.
+	// if it set to non-zero, it represents that the operator swap out as the primary storage provider
+	// it it set to zero, it represents that the operator swap out as the secondary storage provider.
+	GlobalVirtualGroupFamilyId uint32 `protobuf:"varint,2,opt,name=global_virtual_group_family_id,json=globalVirtualGroupFamilyId,proto3" json:"global_virtual_group_family_id,omitempty"`
+	// global_virtual_group_ids is a list of global virtual group IDs associated with the swap out.
+	// It allows to be empty only when the operator is the primary storage provider.
+	GlobalVirtualGroupIds []uint32 `protobuf:"varint,3,rep,packed,name=global_virtual_group_ids,json=globalVirtualGroupIds,proto3" json:"global_virtual_group_ids,omitempty"`
+}
+
+func (m *MsgCancelSwapOut) Reset()         { *m = MsgCancelSwapOut{} }
+func (m *MsgCancelSwapOut) String() string { return proto.CompactTextString(m) }
+func (*MsgCancelSwapOut) ProtoMessage()    {}
+func (*MsgCancelSwapOut) Descriptor() ([]byte, []int) {
+	return fileDescriptor_478f7001009bf3f2, []int{14}
+}
+func (m *MsgCancelSwapOut) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgCancelSwapOut) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgCancelSwapOut.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MsgCancelSwapOut) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgCancelSwapOut.Merge(m, src)
+}
+func (m *MsgCancelSwapOut) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgCancelSwapOut) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgCancelSwapOut.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgCancelSwapOut proto.InternalMessageInfo
+
+func (m *MsgCancelSwapOut) GetStorageProvider() string {
+	if m != nil {
+		return m.StorageProvider
+	}
+	return ""
+}
+
+func (m *MsgCancelSwapOut) GetGlobalVirtualGroupFamilyId() uint32 {
+	if m != nil {
+		return m.GlobalVirtualGroupFamilyId
+	}
+	return 0
+}
+
+func (m *MsgCancelSwapOut) GetGlobalVirtualGroupIds() []uint32 {
+	if m != nil {
+		return m.GlobalVirtualGroupIds
+	}
+	return nil
+}
+
+type MsgCancelSwapOutResponse struct {
+}
+
+func (m *MsgCancelSwapOutResponse) Reset()         { *m = MsgCancelSwapOutResponse{} }
+func (m *MsgCancelSwapOutResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgCancelSwapOutResponse) ProtoMessage()    {}
+func (*MsgCancelSwapOutResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_478f7001009bf3f2, []int{15}
+}
+func (m *MsgCancelSwapOutResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgCancelSwapOutResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgCancelSwapOutResponse.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MsgCancelSwapOutResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgCancelSwapOutResponse.Merge(m, src)
+}
+func (m *MsgCancelSwapOutResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgCancelSwapOutResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgCancelSwapOutResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgCancelSwapOutResponse proto.InternalMessageInfo
+
+// MsgSettle define the message for settling storage income of GVG family or several GVGs.
+// Firstly, the handler will do stream settlement for the payment account; and
+// secondly, the income will be distributed to related storage providers.
+type MsgSettle struct {
+	// storage_provider defines the operator/funding account address of the storage provider who initial settle request.
+	StorageProvider string `protobuf:"bytes,1,opt,name=storage_provider,json=storageProvider,proto3" json:"storage_provider,omitempty"`
+	// global_virtual_group_family_id is the identifier of the global virtual group family.
+	GlobalVirtualGroupFamilyId uint32 `protobuf:"varint,2,opt,name=global_virtual_group_family_id,json=globalVirtualGroupFamilyId,proto3" json:"global_virtual_group_family_id,omitempty"`
+	// global_virtual_group_id is the identifier of the global virtual group.
+	GlobalVirtualGroupIds []uint32 `protobuf:"varint,3,rep,packed,name=global_virtual_group_ids,json=globalVirtualGroupIds,proto3" json:"global_virtual_group_ids,omitempty"`
+}
+
+func (m *MsgSettle) Reset()         { *m = MsgSettle{} }
+func (m *MsgSettle) String() string { return proto.CompactTextString(m) }
+func (*MsgSettle) ProtoMessage()    {}
+func (*MsgSettle) Descriptor() ([]byte, []int) {
+	return fileDescriptor_478f7001009bf3f2, []int{16}
+}
+func (m *MsgSettle) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgSettle) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgSettle.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MsgSettle) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgSettle.Merge(m, src)
+}
+func (m *MsgSettle) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgSettle) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgSettle.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgSettle proto.InternalMessageInfo
+
+func (m *MsgSettle) GetStorageProvider() string {
+	if m != nil {
+		return m.StorageProvider
+	}
+	return ""
+}
+
+func (m *MsgSettle) GetGlobalVirtualGroupFamilyId() uint32 {
+	if m != nil {
+		return m.GlobalVirtualGroupFamilyId
+	}
+	return 0
+}
+
+func (m *MsgSettle) GetGlobalVirtualGroupIds() []uint32 {
+	if m != nil {
+		return m.GlobalVirtualGroupIds
+	}
+	return nil
+}
+
+type MsgSettleResponse struct {
+}
+
+func (m *MsgSettleResponse) Reset()         { *m = MsgSettleResponse{} }
+func (m *MsgSettleResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgSettleResponse) ProtoMessage()    {}
+func (*MsgSettleResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_478f7001009bf3f2, []int{17}
+}
+func (m *MsgSettleResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgSettleResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgSettleResponse.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MsgSettleResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgSettleResponse.Merge(m, src)
+}
+func (m *MsgSettleResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgSettleResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgSettleResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgSettleResponse proto.InternalMessageInfo
+
+// this line is used by starport scaffolding # proto/tx/message
+type MsgStorageProviderExit struct {
+	// storage_provider defines the operator account address of the storage provider who want to exit from the greenfield storage network.
+	StorageProvider string `protobuf:"bytes,1,opt,name=storage_provider,json=storageProvider,proto3" json:"storage_provider,omitempty"`
+}
+
+func (m *MsgStorageProviderExit) Reset()         { *m = MsgStorageProviderExit{} }
+func (m *MsgStorageProviderExit) String() string { return proto.CompactTextString(m) }
+func (*MsgStorageProviderExit) ProtoMessage()    {}
+func (*MsgStorageProviderExit) Descriptor() ([]byte, []int) {
+	return fileDescriptor_478f7001009bf3f2, []int{18}
+}
+func (m *MsgStorageProviderExit) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgStorageProviderExit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgStorageProviderExit.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MsgStorageProviderExit) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgStorageProviderExit.Merge(m, src)
+}
+func (m *MsgStorageProviderExit) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgStorageProviderExit) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgStorageProviderExit.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgStorageProviderExit proto.InternalMessageInfo
+
+func (m *MsgStorageProviderExit) GetStorageProvider() string {
+	if m != nil {
+		return m.StorageProvider
+	}
+	return ""
+}
+
+type MsgStorageProviderExitResponse struct {
+}
+
+func (m *MsgStorageProviderExitResponse) Reset()         { *m = MsgStorageProviderExitResponse{} }
+func (m *MsgStorageProviderExitResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgStorageProviderExitResponse) ProtoMessage()    {}
+func (*MsgStorageProviderExitResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_478f7001009bf3f2, []int{19}
+}
+func (m *MsgStorageProviderExitResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgStorageProviderExitResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgStorageProviderExitResponse.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MsgStorageProviderExitResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgStorageProviderExitResponse.Merge(m, src)
+}
+func (m *MsgStorageProviderExitResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgStorageProviderExitResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgStorageProviderExitResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgStorageProviderExitResponse proto.InternalMessageInfo
+
+type MsgCompleteStorageProviderExit struct {
+	// storage_provider defines the operator account address of the storage provider who want to exit from the greenfield storage network.
+	StorageProvider string `protobuf:"bytes,1,opt,name=storage_provider,json=storageProvider,proto3" json:"storage_provider,omitempty"`
+}
+
+func (m *MsgCompleteStorageProviderExit) Reset()         { *m = MsgCompleteStorageProviderExit{} }
+func (m *MsgCompleteStorageProviderExit) String() string { return proto.CompactTextString(m) }
+func (*MsgCompleteStorageProviderExit) ProtoMessage()    {}
+func (*MsgCompleteStorageProviderExit) Descriptor() ([]byte, []int) {
+	return fileDescriptor_478f7001009bf3f2, []int{20}
+}
+func (m *MsgCompleteStorageProviderExit) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgCompleteStorageProviderExit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgCompleteStorageProviderExit.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MsgCompleteStorageProviderExit) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgCompleteStorageProviderExit.Merge(m, src)
+}
+func (m *MsgCompleteStorageProviderExit) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgCompleteStorageProviderExit) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgCompleteStorageProviderExit.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgCompleteStorageProviderExit proto.InternalMessageInfo
+
+func (m *MsgCompleteStorageProviderExit) GetStorageProvider() string {
+	if m != nil {
+		return m.StorageProvider
+	}
+	return ""
+}
+
+type MsgCompleteStorageProviderExitResponse struct {
+}
+
+func (m *MsgCompleteStorageProviderExitResponse) Reset() {
+	*m = MsgCompleteStorageProviderExitResponse{}
+}
+func (m *MsgCompleteStorageProviderExitResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgCompleteStorageProviderExitResponse) ProtoMessage()    {}
+func (*MsgCompleteStorageProviderExitResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_478f7001009bf3f2, []int{21}
+}
+func (m *MsgCompleteStorageProviderExitResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgCompleteStorageProviderExitResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgCompleteStorageProviderExitResponse.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *MsgCompleteStorageProviderExitResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgCompleteStorageProviderExitResponse.Merge(m, src)
+}
+func (m *MsgCompleteStorageProviderExitResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgCompleteStorageProviderExitResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgCompleteStorageProviderExitResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgCompleteStorageProviderExitResponse proto.InternalMessageInfo
+
+func init() {
+	proto.RegisterType((*MsgUpdateParams)(nil), "greenfield.virtualgroup.MsgUpdateParams")
+	proto.RegisterType((*MsgUpdateParamsResponse)(nil), "greenfield.virtualgroup.MsgUpdateParamsResponse")
+	proto.RegisterType((*MsgCreateGlobalVirtualGroup)(nil), "greenfield.virtualgroup.MsgCreateGlobalVirtualGroup")
+	proto.RegisterType((*MsgCreateGlobalVirtualGroupResponse)(nil), "greenfield.virtualgroup.MsgCreateGlobalVirtualGroupResponse")
+	proto.RegisterType((*MsgDeleteGlobalVirtualGroup)(nil), "greenfield.virtualgroup.MsgDeleteGlobalVirtualGroup")
+	proto.RegisterType((*MsgDeleteGlobalVirtualGroupResponse)(nil), "greenfield.virtualgroup.MsgDeleteGlobalVirtualGroupResponse")
+	proto.RegisterType((*MsgDeposit)(nil), "greenfield.virtualgroup.MsgDeposit")
+	proto.RegisterType((*MsgDepositResponse)(nil), "greenfield.virtualgroup.MsgDepositResponse")
+	proto.RegisterType((*MsgWithdraw)(nil), "greenfield.virtualgroup.MsgWithdraw")
+	proto.RegisterType((*MsgWithdrawResponse)(nil), "greenfield.virtualgroup.MsgWithdrawResponse")
+	proto.RegisterType((*MsgSwapOut)(nil), "greenfield.virtualgroup.MsgSwapOut")
+	proto.RegisterType((*MsgSwapOutResponse)(nil), "greenfield.virtualgroup.MsgSwapOutResponse")
+	proto.RegisterType((*MsgCompleteSwapOut)(nil), "greenfield.virtualgroup.MsgCompleteSwapOut")
+	proto.RegisterType((*MsgCompleteSwapOutResponse)(nil), "greenfield.virtualgroup.MsgCompleteSwapOutResponse")
+	proto.RegisterType((*MsgCancelSwapOut)(nil), "greenfield.virtualgroup.MsgCancelSwapOut")
+	proto.RegisterType((*MsgCancelSwapOutResponse)(nil), "greenfield.virtualgroup.MsgCancelSwapOutResponse")
+	proto.RegisterType((*MsgSettle)(nil), "greenfield.virtualgroup.MsgSettle")
+	proto.RegisterType((*MsgSettleResponse)(nil), "greenfield.virtualgroup.MsgSettleResponse")
+	proto.RegisterType((*MsgStorageProviderExit)(nil), "greenfield.virtualgroup.MsgStorageProviderExit")
+	proto.RegisterType((*MsgStorageProviderExitResponse)(nil), "greenfield.virtualgroup.MsgStorageProviderExitResponse")
+	proto.RegisterType((*MsgCompleteStorageProviderExit)(nil), "greenfield.virtualgroup.MsgCompleteStorageProviderExit")
+	proto.RegisterType((*MsgCompleteStorageProviderExitResponse)(nil), "greenfield.virtualgroup.MsgCompleteStorageProviderExitResponse")
+}
+
+func init() { proto.RegisterFile("greenfield/virtualgroup/tx.proto", fileDescriptor_478f7001009bf3f2) }
+
+var fileDescriptor_478f7001009bf3f2 = []byte{
+	// 970 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x58, 0xcf, 0x6f, 0xdc, 0x44,
+	0x14, 0x8e, 0x9b, 0x92, 0x26, 0xaf, 0x6c, 0x13, 0x9c, 0x84, 0x38, 0x0e, 0x72, 0x57, 0xdb, 0x52,
+	0x2d, 0x2d, 0xb5, 0x49, 0x5b, 0xa8, 0x28, 0x20, 0xd4, 0x04, 0xa8, 0x72, 0x08, 0xad, 0x36, 0xe2,
+	0x87, 0x40, 0x62, 0x35, 0x6b, 0x4f, 0x1d, 0x23, 0xdb, 0x63, 0x79, 0x66, 0x37, 0x89, 0x84, 0x84,
+	0xc4, 0x81, 0x23, 0xe2, 0x06, 0x7f, 0x46, 0x0f, 0xfc, 0x11, 0x3d, 0x56, 0x9c, 0x10, 0x12, 0x15,
+	0x4a, 0x90, 0x38, 0x20, 0xfe, 0x02, 0x2e, 0xc8, 0xe3, 0xf1, 0xac, 0xb3, 0xbb, 0x9e, 0x6c, 0xab,
+	0x40, 0xa5, 0x9c, 0x92, 0x9d, 0xf9, 0xde, 0x7b, 0xdf, 0xf7, 0xcd, 0xf8, 0xed, 0xf3, 0x42, 0xdd,
+	0x4f, 0x31, 0x8e, 0xef, 0x07, 0x38, 0xf4, 0x9c, 0x5e, 0x90, 0xb2, 0x2e, 0x0a, 0xfd, 0x94, 0x74,
+	0x13, 0x87, 0xed, 0xda, 0x49, 0x4a, 0x18, 0xd1, 0x97, 0xfa, 0x08, 0xbb, 0x8c, 0x30, 0x2d, 0x97,
+	0xd0, 0x88, 0x50, 0xa7, 0x83, 0x28, 0x76, 0x7a, 0xab, 0x1d, 0xcc, 0xd0, 0xaa, 0xe3, 0x92, 0x20,
+	0xce, 0x03, 0xcd, 0x25, 0xb1, 0x1f, 0x51, 0xdf, 0xe9, 0xad, 0x66, 0x7f, 0xc4, 0xc6, 0x72, 0xbe,
+	0xd1, 0xe6, 0x9f, 0x9c, 0xfc, 0x83, 0xd8, 0x5a, 0xf0, 0x89, 0x4f, 0xf2, 0xf5, 0xec, 0x3f, 0xb1,
+	0x5a, 0x26, 0xe9, 0x92, 0x28, 0x22, 0xb1, 0x83, 0x92, 0x24, 0x25, 0x3d, 0x14, 0x0a, 0xc4, 0xc5,
+	0x2a, 0x19, 0x09, 0x4a, 0x51, 0x24, 0xb2, 0x37, 0x7e, 0xd4, 0x60, 0x76, 0x93, 0xfa, 0x1f, 0x25,
+	0x1e, 0x62, 0xf8, 0x1e, 0xdf, 0xd1, 0xdf, 0x80, 0x19, 0xd4, 0x65, 0xdb, 0x24, 0x0d, 0xd8, 0x9e,
+	0xa1, 0xd5, 0xb5, 0xe6, 0xcc, 0x9a, 0xf1, 0xf3, 0x4f, 0x57, 0x17, 0x04, 0xad, 0xdb, 0x9e, 0x97,
+	0x62, 0x4a, 0xb7, 0x58, 0x1a, 0xc4, 0x7e, 0xab, 0x0f, 0xd5, 0xdf, 0x81, 0xa9, 0x3c, 0xb7, 0x71,
+	0xaa, 0xae, 0x35, 0xcf, 0x5e, 0x3b, 0x6f, 0x57, 0xf8, 0x64, 0xe7, 0x85, 0xd6, 0x4e, 0x3f, 0x7c,
+	0x7c, 0x7e, 0xa2, 0x25, 0x82, 0x6e, 0x9d, 0xfb, 0xe6, 0xcf, 0x07, 0x97, 0xfb, 0xe9, 0x1a, 0xcb,
+	0xb0, 0x34, 0xc0, 0xac, 0x85, 0x69, 0x42, 0x62, 0x8a, 0x1b, 0xff, 0x68, 0xb0, 0xb2, 0x49, 0xfd,
+	0xf5, 0x14, 0x23, 0x86, 0xef, 0x84, 0xa4, 0x83, 0xc2, 0x8f, 0xf3, 0xfc, 0x77, 0xb2, 0xfc, 0xfa,
+	0x3a, 0xcc, 0x51, 0x46, 0x52, 0xe4, 0xe3, 0xcc, 0xd1, 0x5e, 0xe0, 0xe1, 0xf4, 0x48, 0x21, 0xb3,
+	0x22, 0xe2, 0x9e, 0x08, 0xd0, 0x57, 0x60, 0xe6, 0x3e, 0x8a, 0x82, 0x70, 0xaf, 0x1d, 0x78, 0x5c,
+	0x51, 0xad, 0x35, 0x9d, 0x2f, 0x6c, 0x78, 0x7a, 0x13, 0xe6, 0x28, 0x76, 0x49, 0xec, 0xa1, 0x74,
+	0xaf, 0x4d, 0x93, 0x76, 0xe0, 0x51, 0x63, 0xb2, 0x3e, 0xd9, 0xac, 0xb5, 0xce, 0xc9, 0xf5, 0xad,
+	0x64, 0xc3, 0xa3, 0xfa, 0x9b, 0x70, 0xc6, 0xc3, 0x09, 0xa1, 0x01, 0x33, 0x4e, 0x73, 0x5b, 0x96,
+	0x6d, 0x51, 0x3f, 0xbb, 0x25, 0xb6, 0xb8, 0x25, 0xf6, 0x3a, 0x09, 0x62, 0x61, 0x48, 0x81, 0xbf,
+	0xb5, 0x98, 0x39, 0x32, 0xa4, 0xa4, 0xf1, 0x32, 0x5c, 0x50, 0x88, 0x97, 0x26, 0x3d, 0xc8, 0x4d,
+	0x7a, 0x0f, 0x87, 0xf8, 0xbf, 0x33, 0xe9, 0x75, 0x58, 0xf2, 0x79, 0xea, 0xb6, 0x38, 0xe0, 0x36,
+	0x3f, 0xe1, 0xbe, 0x65, 0x0b, 0xfe, 0x50, 0xe5, 0x0d, 0x4f, 0xad, 0xac, 0x8a, 0xb1, 0x54, 0xf6,
+	0xab, 0x06, 0xc0, 0x71, 0xdc, 0xa6, 0x67, 0x29, 0xa4, 0x7c, 0xba, 0x93, 0xc7, 0x73, 0xba, 0x0b,
+	0xa0, 0xf7, 0xb5, 0x49, 0xc9, 0xbf, 0x69, 0x70, 0x76, 0x93, 0xfa, 0x9f, 0x04, 0x6c, 0xdb, 0x4b,
+	0xd1, 0xce, 0x33, 0xd5, 0xfc, 0x16, 0x4c, 0xef, 0x08, 0x1e, 0xe3, 0x8a, 0x96, 0x01, 0x55, 0xaa,
+	0x17, 0x61, 0xbe, 0x24, 0x4f, 0xca, 0x7e, 0x7c, 0x8a, 0x9f, 0xf4, 0xd6, 0x0e, 0x4a, 0xee, 0x76,
+	0x8f, 0xe9, 0xa4, 0xd7, 0xc0, 0x1a, 0xa9, 0x7a, 0xf0, 0x61, 0x37, 0x87, 0xc5, 0x7f, 0x50, 0x3c,
+	0xfe, 0x37, 0xc1, 0xa8, 0x70, 0xae, 0x68, 0x03, 0x8b, 0xa3, 0xac, 0xa3, 0xfa, 0x25, 0x98, 0xa5,
+	0x5d, 0xd7, 0xc5, 0x94, 0x92, 0x34, 0xef, 0x1b, 0xbc, 0x2b, 0xd4, 0x5a, 0x35, 0xb9, 0x9c, 0xb5,
+	0x0d, 0xfd, 0x2e, 0x2c, 0x1e, 0xc2, 0x15, 0xcd, 0xdd, 0x78, 0x8e, 0x1b, 0xbe, 0x52, 0x6e, 0xad,
+	0x79, 0xff, 0xb7, 0x6f, 0x0b, 0x48, 0x6b, 0xbe, 0x94, 0xaa, 0x58, 0x54, 0xdf, 0x36, 0xe1, 0xaf,
+	0xb4, 0xfd, 0x6f, 0x8d, 0x2f, 0xaf, 0x93, 0x28, 0xc9, 0x1e, 0xc5, 0x13, 0x63, 0x7f, 0x95, 0x0b,
+	0x2f, 0x81, 0x39, 0x2c, 0x57, 0xba, 0xf1, 0x97, 0x06, 0x73, 0xd9, 0x36, 0x8a, 0x5d, 0x1c, 0x9e,
+	0x78, 0x2f, 0x4c, 0x30, 0x06, 0xc5, 0x4a, 0x27, 0xfe, 0xd0, 0x60, 0x26, 0xbb, 0x2e, 0x98, 0xb1,
+	0x10, 0x9f, 0x5c, 0x0b, 0xe6, 0xe1, 0x05, 0xa9, 0x52, 0x6a, 0x67, 0xf0, 0x62, 0xb6, 0x78, 0x98,
+	0xfe, 0xfb, 0xbb, 0xc7, 0xf4, 0xfd, 0x53, 0x45, 0xa5, 0x0e, 0xd6, 0xe8, 0xaa, 0x92, 0xd7, 0x57,
+	0x1c, 0x21, 0xef, 0xee, 0xff, 0xcc, 0xaf, 0x09, 0x97, 0xd4, 0xd5, 0x0b, 0x9e, 0xd7, 0xbe, 0x05,
+	0x98, 0xdc, 0xa4, 0xbe, 0xfe, 0x9d, 0x06, 0x46, 0xe5, 0xe0, 0x76, 0xa3, 0x72, 0x64, 0x54, 0x4c,
+	0x3c, 0xe6, 0xdb, 0x4f, 0x13, 0x55, 0x10, 0xe3, 0x84, 0x2a, 0x87, 0x24, 0x25, 0xa1, 0xaa, 0x28,
+	0x35, 0xa1, 0xa3, 0xc6, 0x1b, 0xfd, 0x73, 0x38, 0x53, 0x8c, 0x36, 0x17, 0xd4, 0x89, 0x38, 0xc8,
+	0xbc, 0x32, 0x06, 0x48, 0x26, 0xff, 0x02, 0xa6, 0xe5, 0x10, 0x71, 0x51, 0x15, 0x58, 0xa0, 0xcc,
+	0x57, 0xc7, 0x41, 0x95, 0xc9, 0x17, 0x2d, 0x52, 0x49, 0x5e, 0x80, 0xd4, 0xe4, 0x07, 0xfa, 0x8f,
+	0xfe, 0x29, 0x4c, 0x89, 0xde, 0xd3, 0x50, 0x86, 0x71, 0x8c, 0x79, 0xf9, 0x68, 0x8c, 0xcc, 0xfc,
+	0x25, 0x3c, 0x7f, 0xe8, 0x1d, 0xa8, 0xa9, 0x8a, 0x2d, 0x23, 0xcd, 0xd7, 0xc6, 0x45, 0xca, 0x5a,
+	0x5f, 0xc3, 0xfc, 0xa8, 0xc7, 0xd4, 0x51, 0xd2, 0x1d, 0x0e, 0x30, 0x6f, 0x3e, 0x61, 0x80, 0x24,
+	0xf0, 0x83, 0x06, 0x2b, 0xaa, 0x86, 0xa1, 0x4c, 0xac, 0x08, 0x34, 0xdf, 0x7d, 0xca, 0x40, 0xc9,
+	0x8c, 0xc2, 0xec, 0xe0, 0xd0, 0x71, 0x65, 0xac, 0x9c, 0xe2, 0x36, 0x5d, 0x7f, 0x02, 0xb0, 0x2c,
+	0x1a, 0x41, 0xed, 0xf0, 0x77, 0xfb, 0x2b, 0xca, 0x2c, 0x65, 0xa8, 0xb9, 0x3a, 0x36, 0xb4, 0x28,
+	0xb7, 0xf6, 0xe1, 0xc3, 0x7d, 0x4b, 0x7b, 0xb4, 0x6f, 0x69, 0xbf, 0xef, 0x5b, 0xda, 0xf7, 0x07,
+	0xd6, 0xc4, 0xa3, 0x03, 0x6b, 0xe2, 0x97, 0x03, 0x6b, 0xe2, 0xb3, 0x1b, 0x7e, 0xc0, 0xb6, 0xbb,
+	0x9d, 0x6c, 0xa0, 0x73, 0x3a, 0x71, 0xe7, 0xaa, 0xbb, 0x8d, 0x82, 0xd8, 0x29, 0xbd, 0xc7, 0xef,
+	0x0e, 0xfc, 0x20, 0xb1, 0x97, 0x60, 0xda, 0x99, 0xe2, 0x6f, 0xf2, 0xd7, 0xff, 0x0d, 0x00, 0x00,
+	0xff, 0xff, 0xc0, 0xc3, 0x09, 0x30, 0xb8, 0x10, 0x00, 0x00,
+}
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConn
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+const _ = grpc.SupportPackageIsVersion4
+
+// MsgClient is the client API for Msg service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
+type MsgClient interface {
+	// this line is used by starport scaffolding # proto/tx/import
+	CreateGlobalVirtualGroup(ctx context.Context, in *MsgCreateGlobalVirtualGroup, opts ...grpc.CallOption) (*MsgCreateGlobalVirtualGroupResponse, error)
+	DeleteGlobalVirtualGroup(ctx context.Context, in *MsgDeleteGlobalVirtualGroup, opts ...grpc.CallOption) (*MsgDeleteGlobalVirtualGroupResponse, error)
+	Deposit(ctx context.Context, in *MsgDeposit, opts ...grpc.CallOption) (*MsgDepositResponse, error)
+	Withdraw(ctx context.Context, in *MsgWithdraw, opts ...grpc.CallOption) (*MsgWithdrawResponse, error)
+	SwapOut(ctx context.Context, in *MsgSwapOut, opts ...grpc.CallOption) (*MsgSwapOutResponse, error)
+	Settle(ctx context.Context, in *MsgSettle, opts ...grpc.CallOption) (*MsgSettleResponse, error)
+	// Since: cosmos-sdk 0.47
+	UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error)
+	StorageProviderExit(ctx context.Context, in *MsgStorageProviderExit, opts ...grpc.CallOption) (*MsgStorageProviderExitResponse, error)
+	CompleteStorageProviderExit(ctx context.Context, in *MsgCompleteStorageProviderExit, opts ...grpc.CallOption) (*MsgCompleteStorageProviderExitResponse, error)
+	CompleteSwapOut(ctx context.Context, in *MsgCompleteSwapOut, opts ...grpc.CallOption) (*MsgCompleteSwapOutResponse, error)
+	CancelSwapOut(ctx context.Context, in *MsgCancelSwapOut, opts ...grpc.CallOption) (*MsgCancelSwapOutResponse, error)
+}
+
+type msgClient struct {
+	cc grpc1.ClientConn
+}
+
+func NewMsgClient(cc grpc1.ClientConn) MsgClient {
+	return &msgClient{cc}
+}
+
+func (c *msgClient) CreateGlobalVirtualGroup(ctx context.Context, in *MsgCreateGlobalVirtualGroup, opts ...grpc.CallOption) (*MsgCreateGlobalVirtualGroupResponse, error) {
+	out := new(MsgCreateGlobalVirtualGroupResponse)
+	err := c.cc.Invoke(ctx, "/greenfield.virtualgroup.Msg/CreateGlobalVirtualGroup", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *msgClient) DeleteGlobalVirtualGroup(ctx context.Context, in *MsgDeleteGlobalVirtualGroup, opts ...grpc.CallOption) (*MsgDeleteGlobalVirtualGroupResponse, error) {
+	out := new(MsgDeleteGlobalVirtualGroupResponse)
+	err := c.cc.Invoke(ctx, "/greenfield.virtualgroup.Msg/DeleteGlobalVirtualGroup", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *msgClient) Deposit(ctx context.Context, in *MsgDeposit, opts ...grpc.CallOption) (*MsgDepositResponse, error) {
+	out := new(MsgDepositResponse)
+	err := c.cc.Invoke(ctx, "/greenfield.virtualgroup.Msg/Deposit", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *msgClient) Withdraw(ctx context.Context, in *MsgWithdraw, opts ...grpc.CallOption) (*MsgWithdrawResponse, error) {
+	out := new(MsgWithdrawResponse)
+	err := c.cc.Invoke(ctx, "/greenfield.virtualgroup.Msg/Withdraw", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *msgClient) SwapOut(ctx context.Context, in *MsgSwapOut, opts ...grpc.CallOption) (*MsgSwapOutResponse, error) {
+	out := new(MsgSwapOutResponse)
+	err := c.cc.Invoke(ctx, "/greenfield.virtualgroup.Msg/SwapOut", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *msgClient) Settle(ctx context.Context, in *MsgSettle, opts ...grpc.CallOption) (*MsgSettleResponse, error) {
+	out := new(MsgSettleResponse)
+	err := c.cc.Invoke(ctx, "/greenfield.virtualgroup.Msg/Settle", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) {
+	out := new(MsgUpdateParamsResponse)
+	err := c.cc.Invoke(ctx, "/greenfield.virtualgroup.Msg/UpdateParams", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *msgClient) StorageProviderExit(ctx context.Context, in *MsgStorageProviderExit, opts ...grpc.CallOption) (*MsgStorageProviderExitResponse, error) {
+	out := new(MsgStorageProviderExitResponse)
+	err := c.cc.Invoke(ctx, "/greenfield.virtualgroup.Msg/StorageProviderExit", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *msgClient) CompleteStorageProviderExit(ctx context.Context, in *MsgCompleteStorageProviderExit, opts ...grpc.CallOption) (*MsgCompleteStorageProviderExitResponse, error) {
+	out := new(MsgCompleteStorageProviderExitResponse)
+	err := c.cc.Invoke(ctx, "/greenfield.virtualgroup.Msg/CompleteStorageProviderExit", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *msgClient) CompleteSwapOut(ctx context.Context, in *MsgCompleteSwapOut, opts ...grpc.CallOption) (*MsgCompleteSwapOutResponse, error) {
+	out := new(MsgCompleteSwapOutResponse)
+	err := c.cc.Invoke(ctx, "/greenfield.virtualgroup.Msg/CompleteSwapOut", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *msgClient) CancelSwapOut(ctx context.Context, in *MsgCancelSwapOut, opts ...grpc.CallOption) (*MsgCancelSwapOutResponse, error) {
+	out := new(MsgCancelSwapOutResponse)
+	err := c.cc.Invoke(ctx, "/greenfield.virtualgroup.Msg/CancelSwapOut", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+// MsgServer is the server API for Msg service.
+type MsgServer interface {
+	// this line is used by starport scaffolding # proto/tx/import
+	CreateGlobalVirtualGroup(context.Context, *MsgCreateGlobalVirtualGroup) (*MsgCreateGlobalVirtualGroupResponse, error)
+	DeleteGlobalVirtualGroup(context.Context, *MsgDeleteGlobalVirtualGroup) (*MsgDeleteGlobalVirtualGroupResponse, error)
+	Deposit(context.Context, *MsgDeposit) (*MsgDepositResponse, error)
+	Withdraw(context.Context, *MsgWithdraw) (*MsgWithdrawResponse, error)
+	SwapOut(context.Context, *MsgSwapOut) (*MsgSwapOutResponse, error)
+	Settle(context.Context, *MsgSettle) (*MsgSettleResponse, error)
+	// Since: cosmos-sdk 0.47
+	UpdateParams(context.Context, *MsgUpdateParams) (*MsgUpdateParamsResponse, error)
+	StorageProviderExit(context.Context, *MsgStorageProviderExit) (*MsgStorageProviderExitResponse, error)
+	CompleteStorageProviderExit(context.Context, *MsgCompleteStorageProviderExit) (*MsgCompleteStorageProviderExitResponse, error)
+	CompleteSwapOut(context.Context, *MsgCompleteSwapOut) (*MsgCompleteSwapOutResponse, error)
+	CancelSwapOut(context.Context, *MsgCancelSwapOut) (*MsgCancelSwapOutResponse, error)
+}
+
+// UnimplementedMsgServer can be embedded to have forward compatible implementations.
+type UnimplementedMsgServer struct {
+}
+
+func (*UnimplementedMsgServer) CreateGlobalVirtualGroup(ctx context.Context, req *MsgCreateGlobalVirtualGroup) (*MsgCreateGlobalVirtualGroupResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method CreateGlobalVirtualGroup not implemented")
+}
+func (*UnimplementedMsgServer) DeleteGlobalVirtualGroup(ctx context.Context, req *MsgDeleteGlobalVirtualGroup) (*MsgDeleteGlobalVirtualGroupResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method DeleteGlobalVirtualGroup not implemented")
+}
+func (*UnimplementedMsgServer) Deposit(ctx context.Context, req *MsgDeposit) (*MsgDepositResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Deposit not implemented")
+}
+func (*UnimplementedMsgServer) Withdraw(ctx context.Context, req *MsgWithdraw) (*MsgWithdrawResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Withdraw not implemented")
+}
+func (*UnimplementedMsgServer) SwapOut(ctx context.Context, req *MsgSwapOut) (*MsgSwapOutResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method SwapOut not implemented")
+}
+func (*UnimplementedMsgServer) Settle(ctx context.Context, req *MsgSettle) (*MsgSettleResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Settle not implemented")
+}
+func (*UnimplementedMsgServer) UpdateParams(ctx context.Context, req *MsgUpdateParams) (*MsgUpdateParamsResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method UpdateParams not implemented")
+}
+func (*UnimplementedMsgServer) StorageProviderExit(ctx context.Context, req *MsgStorageProviderExit) (*MsgStorageProviderExitResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method StorageProviderExit not implemented")
+}
+func (*UnimplementedMsgServer) CompleteStorageProviderExit(ctx context.Context, req *MsgCompleteStorageProviderExit) (*MsgCompleteStorageProviderExitResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method CompleteStorageProviderExit not implemented")
+}
+func (*UnimplementedMsgServer) CompleteSwapOut(ctx context.Context, req *MsgCompleteSwapOut) (*MsgCompleteSwapOutResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method CompleteSwapOut not implemented")
+}
+func (*UnimplementedMsgServer) CancelSwapOut(ctx context.Context, req *MsgCancelSwapOut) (*MsgCancelSwapOutResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method CancelSwapOut not implemented")
+}
+
+func RegisterMsgServer(s grpc1.Server, srv MsgServer) {
+	s.RegisterService(&_Msg_serviceDesc, srv)
+}
+
+func _Msg_CreateGlobalVirtualGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(MsgCreateGlobalVirtualGroup)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(MsgServer).CreateGlobalVirtualGroup(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/greenfield.virtualgroup.Msg/CreateGlobalVirtualGroup",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(MsgServer).CreateGlobalVirtualGroup(ctx, req.(*MsgCreateGlobalVirtualGroup))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Msg_DeleteGlobalVirtualGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(MsgDeleteGlobalVirtualGroup)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(MsgServer).DeleteGlobalVirtualGroup(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/greenfield.virtualgroup.Msg/DeleteGlobalVirtualGroup",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(MsgServer).DeleteGlobalVirtualGroup(ctx, req.(*MsgDeleteGlobalVirtualGroup))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Msg_Deposit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(MsgDeposit)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(MsgServer).Deposit(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/greenfield.virtualgroup.Msg/Deposit",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(MsgServer).Deposit(ctx, req.(*MsgDeposit))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Msg_Withdraw_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(MsgWithdraw)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(MsgServer).Withdraw(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/greenfield.virtualgroup.Msg/Withdraw",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(MsgServer).Withdraw(ctx, req.(*MsgWithdraw))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Msg_SwapOut_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(MsgSwapOut)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(MsgServer).SwapOut(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/greenfield.virtualgroup.Msg/SwapOut",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(MsgServer).SwapOut(ctx, req.(*MsgSwapOut))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Msg_Settle_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(MsgSettle)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(MsgServer).Settle(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/greenfield.virtualgroup.Msg/Settle",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(MsgServer).Settle(ctx, req.(*MsgSettle))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Msg_UpdateParams_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(MsgUpdateParams)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(MsgServer).UpdateParams(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/greenfield.virtualgroup.Msg/UpdateParams",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(MsgServer).UpdateParams(ctx, req.(*MsgUpdateParams))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Msg_StorageProviderExit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(MsgStorageProviderExit)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(MsgServer).StorageProviderExit(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/greenfield.virtualgroup.Msg/StorageProviderExit",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(MsgServer).StorageProviderExit(ctx, req.(*MsgStorageProviderExit))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Msg_CompleteStorageProviderExit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(MsgCompleteStorageProviderExit)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(MsgServer).CompleteStorageProviderExit(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/greenfield.virtualgroup.Msg/CompleteStorageProviderExit",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(MsgServer).CompleteStorageProviderExit(ctx, req.(*MsgCompleteStorageProviderExit))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Msg_CompleteSwapOut_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(MsgCompleteSwapOut)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(MsgServer).CompleteSwapOut(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/greenfield.virtualgroup.Msg/CompleteSwapOut",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(MsgServer).CompleteSwapOut(ctx, req.(*MsgCompleteSwapOut))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Msg_CancelSwapOut_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(MsgCancelSwapOut)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(MsgServer).CancelSwapOut(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/greenfield.virtualgroup.Msg/CancelSwapOut",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(MsgServer).CancelSwapOut(ctx, req.(*MsgCancelSwapOut))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+var _Msg_serviceDesc = grpc.ServiceDesc{
+	ServiceName: "greenfield.virtualgroup.Msg",
+	HandlerType: (*MsgServer)(nil),
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "CreateGlobalVirtualGroup",
+			Handler:    _Msg_CreateGlobalVirtualGroup_Handler,
+		},
+		{
+			MethodName: "DeleteGlobalVirtualGroup",
+			Handler:    _Msg_DeleteGlobalVirtualGroup_Handler,
+		},
+		{
+			MethodName: "Deposit",
+			Handler:    _Msg_Deposit_Handler,
+		},
+		{
+			MethodName: "Withdraw",
+			Handler:    _Msg_Withdraw_Handler,
+		},
+		{
+			MethodName: "SwapOut",
+			Handler:    _Msg_SwapOut_Handler,
+		},
+		{
+			MethodName: "Settle",
+			Handler:    _Msg_Settle_Handler,
+		},
+		{
+			MethodName: "UpdateParams",
+			Handler:    _Msg_UpdateParams_Handler,
+		},
+		{
+			MethodName: "StorageProviderExit",
+			Handler:    _Msg_StorageProviderExit_Handler,
+		},
+		{
+			MethodName: "CompleteStorageProviderExit",
+			Handler:    _Msg_CompleteStorageProviderExit_Handler,
+		},
+		{
+			MethodName: "CompleteSwapOut",
+			Handler:    _Msg_CompleteSwapOut_Handler,
+		},
+		{
+			MethodName: "CancelSwapOut",
+			Handler:    _Msg_CancelSwapOut_Handler,
+		},
+	},
+	Streams:  []grpc.StreamDesc{},
+	Metadata: "greenfield/virtualgroup/tx.proto",
+}
+
+func (m *MsgUpdateParams) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MsgUpdateParams) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgUpdateParams) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	{
+		size, err := m.Params.MarshalToSizedBuffer(dAtA[:i])
+		if err != nil {
+			return 0, err
+		}
+		i -= size
+		i = encodeVarintTx(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0x12
+	if len(m.Authority) > 0 {
+		i -= len(m.Authority)
+		copy(dAtA[i:], m.Authority)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.Authority)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgUpdateParamsResponse) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MsgUpdateParamsResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgUpdateParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgCreateGlobalVirtualGroup) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MsgCreateGlobalVirtualGroup) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgCreateGlobalVirtualGroup) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	{
+		size, err := m.Deposit.MarshalToSizedBuffer(dAtA[:i])
+		if err != nil {
+			return 0, err
+		}
+		i -= size
+		i = encodeVarintTx(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0x22
+	if len(m.SecondarySpIds) > 0 {
+		dAtA4 := make([]byte, len(m.SecondarySpIds)*10)
+		var j3 int
+		for _, num := range m.SecondarySpIds {
+			for num >= 1<<7 {
+				dAtA4[j3] = uint8(uint64(num)&0x7f | 0x80)
+				num >>= 7
+				j3++
+			}
+			dAtA4[j3] = uint8(num)
+			j3++
+		}
+		i -= j3
+		copy(dAtA[i:], dAtA4[:j3])
+		i = encodeVarintTx(dAtA, i, uint64(j3))
+		i--
+		dAtA[i] = 0x1a
+	}
+	if m.FamilyId != 0 {
+		i = encodeVarintTx(dAtA, i, uint64(m.FamilyId))
+		i--
+		dAtA[i] = 0x10
+	}
+	if len(m.StorageProvider) > 0 {
+		i -= len(m.StorageProvider)
+		copy(dAtA[i:], m.StorageProvider)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.StorageProvider)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgCreateGlobalVirtualGroupResponse) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MsgCreateGlobalVirtualGroupResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgCreateGlobalVirtualGroupResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgDeleteGlobalVirtualGroup) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MsgDeleteGlobalVirtualGroup) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgDeleteGlobalVirtualGroup) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.GlobalVirtualGroupId != 0 {
+		i = encodeVarintTx(dAtA, i, uint64(m.GlobalVirtualGroupId))
+		i--
+		dAtA[i] = 0x10
+	}
+	if len(m.StorageProvider) > 0 {
+		i -= len(m.StorageProvider)
+		copy(dAtA[i:], m.StorageProvider)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.StorageProvider)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgDeleteGlobalVirtualGroupResponse) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MsgDeleteGlobalVirtualGroupResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgDeleteGlobalVirtualGroupResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgDeposit) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MsgDeposit) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgDeposit) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	{
+		size, err := m.Deposit.MarshalToSizedBuffer(dAtA[:i])
+		if err != nil {
+			return 0, err
+		}
+		i -= size
+		i = encodeVarintTx(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0x1a
+	if m.GlobalVirtualGroupId != 0 {
+		i = encodeVarintTx(dAtA, i, uint64(m.GlobalVirtualGroupId))
+		i--
+		dAtA[i] = 0x10
+	}
+	if len(m.StorageProvider) > 0 {
+		i -= len(m.StorageProvider)
+		copy(dAtA[i:], m.StorageProvider)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.StorageProvider)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgDepositResponse) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MsgDepositResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgDepositResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgWithdraw) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MsgWithdraw) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgWithdraw) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	{
+		size, err := m.Withdraw.MarshalToSizedBuffer(dAtA[:i])
+		if err != nil {
+			return 0, err
+		}
+		i -= size
+		i = encodeVarintTx(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0x1a
+	if m.GlobalVirtualGroupId != 0 {
+		i = encodeVarintTx(dAtA, i, uint64(m.GlobalVirtualGroupId))
+		i--
+		dAtA[i] = 0x10
+	}
+	if len(m.StorageProvider) > 0 {
+		i -= len(m.StorageProvider)
+		copy(dAtA[i:], m.StorageProvider)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.StorageProvider)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgWithdrawResponse) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MsgWithdrawResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgWithdrawResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgSwapOut) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MsgSwapOut) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgSwapOut) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.SuccessorSpApproval != nil {
+		{
+			size, err := m.SuccessorSpApproval.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintTx(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0x2a
+	}
+	if m.SuccessorSpId != 0 {
+		i = encodeVarintTx(dAtA, i, uint64(m.SuccessorSpId))
+		i--
+		dAtA[i] = 0x20
+	}
+	if len(m.GlobalVirtualGroupIds) > 0 {
+		dAtA9 := make([]byte, len(m.GlobalVirtualGroupIds)*10)
+		var j8 int
+		for _, num := range m.GlobalVirtualGroupIds {
+			for num >= 1<<7 {
+				dAtA9[j8] = uint8(uint64(num)&0x7f | 0x80)
+				num >>= 7
+				j8++
+			}
+			dAtA9[j8] = uint8(num)
+			j8++
+		}
+		i -= j8
+		copy(dAtA[i:], dAtA9[:j8])
+		i = encodeVarintTx(dAtA, i, uint64(j8))
+		i--
+		dAtA[i] = 0x1a
+	}
+	if m.GlobalVirtualGroupFamilyId != 0 {
+		i = encodeVarintTx(dAtA, i, uint64(m.GlobalVirtualGroupFamilyId))
+		i--
+		dAtA[i] = 0x10
+	}
+	if len(m.StorageProvider) > 0 {
+		i -= len(m.StorageProvider)
+		copy(dAtA[i:], m.StorageProvider)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.StorageProvider)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgSwapOutResponse) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MsgSwapOutResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgSwapOutResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgCompleteSwapOut) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MsgCompleteSwapOut) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgCompleteSwapOut) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.GlobalVirtualGroupIds) > 0 {
+		dAtA11 := make([]byte, len(m.GlobalVirtualGroupIds)*10)
+		var j10 int
+		for _, num := range m.GlobalVirtualGroupIds {
+			for num >= 1<<7 {
+				dAtA11[j10] = uint8(uint64(num)&0x7f | 0x80)
+				num >>= 7
+				j10++
+			}
+			dAtA11[j10] = uint8(num)
+			j10++
+		}
+		i -= j10
+		copy(dAtA[i:], dAtA11[:j10])
+		i = encodeVarintTx(dAtA, i, uint64(j10))
+		i--
+		dAtA[i] = 0x1a
+	}
+	if m.GlobalVirtualGroupFamilyId != 0 {
+		i = encodeVarintTx(dAtA, i, uint64(m.GlobalVirtualGroupFamilyId))
+		i--
+		dAtA[i] = 0x10
+	}
+	if len(m.StorageProvider) > 0 {
+		i -= len(m.StorageProvider)
+		copy(dAtA[i:], m.StorageProvider)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.StorageProvider)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgCompleteSwapOutResponse) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MsgCompleteSwapOutResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgCompleteSwapOutResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgCancelSwapOut) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MsgCancelSwapOut) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgCancelSwapOut) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.GlobalVirtualGroupIds) > 0 {
+		dAtA13 := make([]byte, len(m.GlobalVirtualGroupIds)*10)
+		var j12 int
+		for _, num := range m.GlobalVirtualGroupIds {
+			for num >= 1<<7 {
+				dAtA13[j12] = uint8(uint64(num)&0x7f | 0x80)
+				num >>= 7
+				j12++
+			}
+			dAtA13[j12] = uint8(num)
+			j12++
+		}
+		i -= j12
+		copy(dAtA[i:], dAtA13[:j12])
+		i = encodeVarintTx(dAtA, i, uint64(j12))
+		i--
+		dAtA[i] = 0x1a
+	}
+	if m.GlobalVirtualGroupFamilyId != 0 {
+		i = encodeVarintTx(dAtA, i, uint64(m.GlobalVirtualGroupFamilyId))
+		i--
+		dAtA[i] = 0x10
+	}
+	if len(m.StorageProvider) > 0 {
+		i -= len(m.StorageProvider)
+		copy(dAtA[i:], m.StorageProvider)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.StorageProvider)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgCancelSwapOutResponse) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MsgCancelSwapOutResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgCancelSwapOutResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgSettle) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MsgSettle) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgSettle) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.GlobalVirtualGroupIds) > 0 {
+		dAtA15 := make([]byte, len(m.GlobalVirtualGroupIds)*10)
+		var j14 int
+		for _, num := range m.GlobalVirtualGroupIds {
+			for num >= 1<<7 {
+				dAtA15[j14] = uint8(uint64(num)&0x7f | 0x80)
+				num >>= 7
+				j14++
+			}
+			dAtA15[j14] = uint8(num)
+			j14++
+		}
+		i -= j14
+		copy(dAtA[i:], dAtA15[:j14])
+		i = encodeVarintTx(dAtA, i, uint64(j14))
+		i--
+		dAtA[i] = 0x1a
+	}
+	if m.GlobalVirtualGroupFamilyId != 0 {
+		i = encodeVarintTx(dAtA, i, uint64(m.GlobalVirtualGroupFamilyId))
+		i--
+		dAtA[i] = 0x10
+	}
+	if len(m.StorageProvider) > 0 {
+		i -= len(m.StorageProvider)
+		copy(dAtA[i:], m.StorageProvider)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.StorageProvider)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgSettleResponse) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MsgSettleResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgSettleResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgStorageProviderExit) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MsgStorageProviderExit) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgStorageProviderExit) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.StorageProvider) > 0 {
+		i -= len(m.StorageProvider)
+		copy(dAtA[i:], m.StorageProvider)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.StorageProvider)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgStorageProviderExitResponse) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MsgStorageProviderExitResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgStorageProviderExitResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgCompleteStorageProviderExit) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MsgCompleteStorageProviderExit) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgCompleteStorageProviderExit) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.StorageProvider) > 0 {
+		i -= len(m.StorageProvider)
+		copy(dAtA[i:], m.StorageProvider)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.StorageProvider)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgCompleteStorageProviderExitResponse) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *MsgCompleteStorageProviderExitResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgCompleteStorageProviderExitResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	return len(dAtA) - i, nil
+}
+
+func encodeVarintTx(dAtA []byte, offset int, v uint64) int {
+	offset -= sovTx(v)
+	base := offset
+	for v >= 1<<7 {
+		dAtA[offset] = uint8(v&0x7f | 0x80)
+		v >>= 7
+		offset++
+	}
+	dAtA[offset] = uint8(v)
+	return base
+}
+func (m *MsgUpdateParams) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Authority)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	l = m.Params.Size()
+	n += 1 + l + sovTx(uint64(l))
+	return n
+}
+
+func (m *MsgUpdateParamsResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	return n
+}
+
+func (m *MsgCreateGlobalVirtualGroup) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.StorageProvider)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	if m.FamilyId != 0 {
+		n += 1 + sovTx(uint64(m.FamilyId))
+	}
+	if len(m.SecondarySpIds) > 0 {
+		l = 0
+		for _, e := range m.SecondarySpIds {
+			l += sovTx(uint64(e))
+		}
+		n += 1 + sovTx(uint64(l)) + l
+	}
+	l = m.Deposit.Size()
+	n += 1 + l + sovTx(uint64(l))
+	return n
+}
+
+func (m *MsgCreateGlobalVirtualGroupResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	return n
+}
+
+func (m *MsgDeleteGlobalVirtualGroup) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.StorageProvider)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	if m.GlobalVirtualGroupId != 0 {
+		n += 1 + sovTx(uint64(m.GlobalVirtualGroupId))
+	}
+	return n
+}
+
+func (m *MsgDeleteGlobalVirtualGroupResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	return n
+}
+
+func (m *MsgDeposit) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.StorageProvider)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	if m.GlobalVirtualGroupId != 0 {
+		n += 1 + sovTx(uint64(m.GlobalVirtualGroupId))
+	}
+	l = m.Deposit.Size()
+	n += 1 + l + sovTx(uint64(l))
+	return n
+}
+
+func (m *MsgDepositResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	return n
+}
+
+func (m *MsgWithdraw) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.StorageProvider)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	if m.GlobalVirtualGroupId != 0 {
+		n += 1 + sovTx(uint64(m.GlobalVirtualGroupId))
+	}
+	l = m.Withdraw.Size()
+	n += 1 + l + sovTx(uint64(l))
+	return n
+}
+
+func (m *MsgWithdrawResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	return n
+}
+
+func (m *MsgSwapOut) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.StorageProvider)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	if m.GlobalVirtualGroupFamilyId != 0 {
+		n += 1 + sovTx(uint64(m.GlobalVirtualGroupFamilyId))
+	}
+	if len(m.GlobalVirtualGroupIds) > 0 {
+		l = 0
+		for _, e := range m.GlobalVirtualGroupIds {
+			l += sovTx(uint64(e))
+		}
+		n += 1 + sovTx(uint64(l)) + l
+	}
+	if m.SuccessorSpId != 0 {
+		n += 1 + sovTx(uint64(m.SuccessorSpId))
+	}
+	if m.SuccessorSpApproval != nil {
+		l = m.SuccessorSpApproval.Size()
+		n += 1 + l + sovTx(uint64(l))
+	}
+	return n
+}
+
+func (m *MsgSwapOutResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	return n
+}
+
+func (m *MsgCompleteSwapOut) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.StorageProvider)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	if m.GlobalVirtualGroupFamilyId != 0 {
+		n += 1 + sovTx(uint64(m.GlobalVirtualGroupFamilyId))
+	}
+	if len(m.GlobalVirtualGroupIds) > 0 {
+		l = 0
+		for _, e := range m.GlobalVirtualGroupIds {
+			l += sovTx(uint64(e))
+		}
+		n += 1 + sovTx(uint64(l)) + l
+	}
+	return n
+}
+
+func (m *MsgCompleteSwapOutResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	return n
+}
+
+func (m *MsgCancelSwapOut) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.StorageProvider)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	if m.GlobalVirtualGroupFamilyId != 0 {
+		n += 1 + sovTx(uint64(m.GlobalVirtualGroupFamilyId))
+	}
+	if len(m.GlobalVirtualGroupIds) > 0 {
+		l = 0
+		for _, e := range m.GlobalVirtualGroupIds {
+			l += sovTx(uint64(e))
+		}
+		n += 1 + sovTx(uint64(l)) + l
+	}
+	return n
+}
+
+func (m *MsgCancelSwapOutResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	return n
+}
+
+func (m *MsgSettle) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.StorageProvider)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	if m.GlobalVirtualGroupFamilyId != 0 {
+		n += 1 + sovTx(uint64(m.GlobalVirtualGroupFamilyId))
+	}
+	if len(m.GlobalVirtualGroupIds) > 0 {
+		l = 0
+		for _, e := range m.GlobalVirtualGroupIds {
+			l += sovTx(uint64(e))
+		}
+		n += 1 + sovTx(uint64(l)) + l
+	}
+	return n
+}
+
+func (m *MsgSettleResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	return n
+}
+
+func (m *MsgStorageProviderExit) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.StorageProvider)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	return n
+}
+
+func (m *MsgStorageProviderExitResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	return n
+}
+
+func (m *MsgCompleteStorageProviderExit) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.StorageProvider)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	return n
+}
+
+func (m *MsgCompleteStorageProviderExitResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	return n
+}
+
+func sovTx(x uint64) (n int) {
+	return (math_bits.Len64(x|1) + 6) / 7
+}
+func sozTx(x uint64) (n int) {
+	return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *MsgUpdateParams) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgUpdateParams: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgUpdateParams: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Authority", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Authority = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgUpdateParamsResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgUpdateParamsResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgUpdateParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgCreateGlobalVirtualGroup) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgCreateGlobalVirtualGroup: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgCreateGlobalVirtualGroup: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field StorageProvider", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.StorageProvider = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field FamilyId", wireType)
+			}
+			m.FamilyId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.FamilyId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 3:
+			if wireType == 0 {
+				var v uint32
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowTx
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					v |= uint32(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				m.SecondarySpIds = append(m.SecondarySpIds, v)
+			} else if wireType == 2 {
+				var packedLen int
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowTx
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					packedLen |= int(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				if packedLen < 0 {
+					return ErrInvalidLengthTx
+				}
+				postIndex := iNdEx + packedLen
+				if postIndex < 0 {
+					return ErrInvalidLengthTx
+				}
+				if postIndex > l {
+					return io.ErrUnexpectedEOF
+				}
+				var elementCount int
+				var count int
+				for _, integer := range dAtA[iNdEx:postIndex] {
+					if integer < 128 {
+						count++
+					}
+				}
+				elementCount = count
+				if elementCount != 0 && len(m.SecondarySpIds) == 0 {
+					m.SecondarySpIds = make([]uint32, 0, elementCount)
+				}
+				for iNdEx < postIndex {
+					var v uint32
+					for shift := uint(0); ; shift += 7 {
+						if shift >= 64 {
+							return ErrIntOverflowTx
+						}
+						if iNdEx >= l {
+							return io.ErrUnexpectedEOF
+						}
+						b := dAtA[iNdEx]
+						iNdEx++
+						v |= uint32(b&0x7F) << shift
+						if b < 0x80 {
+							break
+						}
+					}
+					m.SecondarySpIds = append(m.SecondarySpIds, v)
+				}
+			} else {
+				return fmt.Errorf("proto: wrong wireType = %d for field SecondarySpIds", wireType)
+			}
+		case 4:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Deposit", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.Deposit.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgCreateGlobalVirtualGroupResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgCreateGlobalVirtualGroupResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgCreateGlobalVirtualGroupResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgDeleteGlobalVirtualGroup) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgDeleteGlobalVirtualGroup: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgDeleteGlobalVirtualGroup: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field StorageProvider", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.StorageProvider = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupId", wireType)
+			}
+			m.GlobalVirtualGroupId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.GlobalVirtualGroupId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgDeleteGlobalVirtualGroupResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgDeleteGlobalVirtualGroupResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgDeleteGlobalVirtualGroupResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgDeposit) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgDeposit: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgDeposit: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field StorageProvider", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.StorageProvider = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupId", wireType)
+			}
+			m.GlobalVirtualGroupId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.GlobalVirtualGroupId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Deposit", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.Deposit.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgDepositResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgDepositResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgDepositResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgWithdraw) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgWithdraw: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgWithdraw: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field StorageProvider", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.StorageProvider = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupId", wireType)
+			}
+			m.GlobalVirtualGroupId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.GlobalVirtualGroupId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Withdraw", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.Withdraw.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgWithdrawResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgWithdrawResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgWithdrawResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgSwapOut) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgSwapOut: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgSwapOut: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field StorageProvider", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.StorageProvider = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupFamilyId", wireType)
+			}
+			m.GlobalVirtualGroupFamilyId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.GlobalVirtualGroupFamilyId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 3:
+			if wireType == 0 {
+				var v uint32
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowTx
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					v |= uint32(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				m.GlobalVirtualGroupIds = append(m.GlobalVirtualGroupIds, v)
+			} else if wireType == 2 {
+				var packedLen int
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowTx
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					packedLen |= int(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				if packedLen < 0 {
+					return ErrInvalidLengthTx
+				}
+				postIndex := iNdEx + packedLen
+				if postIndex < 0 {
+					return ErrInvalidLengthTx
+				}
+				if postIndex > l {
+					return io.ErrUnexpectedEOF
+				}
+				var elementCount int
+				var count int
+				for _, integer := range dAtA[iNdEx:postIndex] {
+					if integer < 128 {
+						count++
+					}
+				}
+				elementCount = count
+				if elementCount != 0 && len(m.GlobalVirtualGroupIds) == 0 {
+					m.GlobalVirtualGroupIds = make([]uint32, 0, elementCount)
+				}
+				for iNdEx < postIndex {
+					var v uint32
+					for shift := uint(0); ; shift += 7 {
+						if shift >= 64 {
+							return ErrIntOverflowTx
+						}
+						if iNdEx >= l {
+							return io.ErrUnexpectedEOF
+						}
+						b := dAtA[iNdEx]
+						iNdEx++
+						v |= uint32(b&0x7F) << shift
+						if b < 0x80 {
+							break
+						}
+					}
+					m.GlobalVirtualGroupIds = append(m.GlobalVirtualGroupIds, v)
+				}
+			} else {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupIds", wireType)
+			}
+		case 4:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field SuccessorSpId", wireType)
+			}
+			m.SuccessorSpId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.SuccessorSpId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 5:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field SuccessorSpApproval", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.SuccessorSpApproval == nil {
+				m.SuccessorSpApproval = &common.Approval{}
+			}
+			if err := m.SuccessorSpApproval.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgSwapOutResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgSwapOutResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgSwapOutResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgCompleteSwapOut) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgCompleteSwapOut: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgCompleteSwapOut: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field StorageProvider", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.StorageProvider = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupFamilyId", wireType)
+			}
+			m.GlobalVirtualGroupFamilyId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.GlobalVirtualGroupFamilyId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 3:
+			if wireType == 0 {
+				var v uint32
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowTx
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					v |= uint32(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				m.GlobalVirtualGroupIds = append(m.GlobalVirtualGroupIds, v)
+			} else if wireType == 2 {
+				var packedLen int
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowTx
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					packedLen |= int(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				if packedLen < 0 {
+					return ErrInvalidLengthTx
+				}
+				postIndex := iNdEx + packedLen
+				if postIndex < 0 {
+					return ErrInvalidLengthTx
+				}
+				if postIndex > l {
+					return io.ErrUnexpectedEOF
+				}
+				var elementCount int
+				var count int
+				for _, integer := range dAtA[iNdEx:postIndex] {
+					if integer < 128 {
+						count++
+					}
+				}
+				elementCount = count
+				if elementCount != 0 && len(m.GlobalVirtualGroupIds) == 0 {
+					m.GlobalVirtualGroupIds = make([]uint32, 0, elementCount)
+				}
+				for iNdEx < postIndex {
+					var v uint32
+					for shift := uint(0); ; shift += 7 {
+						if shift >= 64 {
+							return ErrIntOverflowTx
+						}
+						if iNdEx >= l {
+							return io.ErrUnexpectedEOF
+						}
+						b := dAtA[iNdEx]
+						iNdEx++
+						v |= uint32(b&0x7F) << shift
+						if b < 0x80 {
+							break
+						}
+					}
+					m.GlobalVirtualGroupIds = append(m.GlobalVirtualGroupIds, v)
+				}
+			} else {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupIds", wireType)
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgCompleteSwapOutResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgCompleteSwapOutResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgCompleteSwapOutResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgCancelSwapOut) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgCancelSwapOut: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgCancelSwapOut: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field StorageProvider", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.StorageProvider = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupFamilyId", wireType)
+			}
+			m.GlobalVirtualGroupFamilyId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.GlobalVirtualGroupFamilyId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 3:
+			if wireType == 0 {
+				var v uint32
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowTx
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					v |= uint32(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				m.GlobalVirtualGroupIds = append(m.GlobalVirtualGroupIds, v)
+			} else if wireType == 2 {
+				var packedLen int
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowTx
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					packedLen |= int(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				if packedLen < 0 {
+					return ErrInvalidLengthTx
+				}
+				postIndex := iNdEx + packedLen
+				if postIndex < 0 {
+					return ErrInvalidLengthTx
+				}
+				if postIndex > l {
+					return io.ErrUnexpectedEOF
+				}
+				var elementCount int
+				var count int
+				for _, integer := range dAtA[iNdEx:postIndex] {
+					if integer < 128 {
+						count++
+					}
+				}
+				elementCount = count
+				if elementCount != 0 && len(m.GlobalVirtualGroupIds) == 0 {
+					m.GlobalVirtualGroupIds = make([]uint32, 0, elementCount)
+				}
+				for iNdEx < postIndex {
+					var v uint32
+					for shift := uint(0); ; shift += 7 {
+						if shift >= 64 {
+							return ErrIntOverflowTx
+						}
+						if iNdEx >= l {
+							return io.ErrUnexpectedEOF
+						}
+						b := dAtA[iNdEx]
+						iNdEx++
+						v |= uint32(b&0x7F) << shift
+						if b < 0x80 {
+							break
+						}
+					}
+					m.GlobalVirtualGroupIds = append(m.GlobalVirtualGroupIds, v)
+				}
+			} else {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupIds", wireType)
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgCancelSwapOutResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgCancelSwapOutResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgCancelSwapOutResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgSettle) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgSettle: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgSettle: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field StorageProvider", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.StorageProvider = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupFamilyId", wireType)
+			}
+			m.GlobalVirtualGroupFamilyId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.GlobalVirtualGroupFamilyId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 3:
+			if wireType == 0 {
+				var v uint32
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowTx
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					v |= uint32(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				m.GlobalVirtualGroupIds = append(m.GlobalVirtualGroupIds, v)
+			} else if wireType == 2 {
+				var packedLen int
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowTx
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					packedLen |= int(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				if packedLen < 0 {
+					return ErrInvalidLengthTx
+				}
+				postIndex := iNdEx + packedLen
+				if postIndex < 0 {
+					return ErrInvalidLengthTx
+				}
+				if postIndex > l {
+					return io.ErrUnexpectedEOF
+				}
+				var elementCount int
+				var count int
+				for _, integer := range dAtA[iNdEx:postIndex] {
+					if integer < 128 {
+						count++
+					}
+				}
+				elementCount = count
+				if elementCount != 0 && len(m.GlobalVirtualGroupIds) == 0 {
+					m.GlobalVirtualGroupIds = make([]uint32, 0, elementCount)
+				}
+				for iNdEx < postIndex {
+					var v uint32
+					for shift := uint(0); ; shift += 7 {
+						if shift >= 64 {
+							return ErrIntOverflowTx
+						}
+						if iNdEx >= l {
+							return io.ErrUnexpectedEOF
+						}
+						b := dAtA[iNdEx]
+						iNdEx++
+						v |= uint32(b&0x7F) << shift
+						if b < 0x80 {
+							break
+						}
+					}
+					m.GlobalVirtualGroupIds = append(m.GlobalVirtualGroupIds, v)
+				}
+			} else {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupIds", wireType)
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgSettleResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgSettleResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgSettleResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgStorageProviderExit) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgStorageProviderExit: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgStorageProviderExit: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field StorageProvider", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.StorageProvider = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgStorageProviderExitResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgStorageProviderExitResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgStorageProviderExitResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgCompleteStorageProviderExit) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgCompleteStorageProviderExit: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgCompleteStorageProviderExit: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field StorageProvider", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.StorageProvider = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgCompleteStorageProviderExitResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgCompleteStorageProviderExitResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgCompleteStorageProviderExitResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func skipTx(dAtA []byte) (n int, err error) {
+	l := len(dAtA)
+	iNdEx := 0
+	depth := 0
+	for iNdEx < l {
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return 0, ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return 0, io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		wireType := int(wire & 0x7)
+		switch wireType {
+		case 0:
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				iNdEx++
+				if dAtA[iNdEx-1] < 0x80 {
+					break
+				}
+			}
+		case 1:
+			iNdEx += 8
+		case 2:
+			var length int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				length |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if length < 0 {
+				return 0, ErrInvalidLengthTx
+			}
+			iNdEx += length
+		case 3:
+			depth++
+		case 4:
+			if depth == 0 {
+				return 0, ErrUnexpectedEndOfGroupTx
+			}
+			depth--
+		case 5:
+			iNdEx += 4
+		default:
+			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+		}
+		if iNdEx < 0 {
+			return 0, ErrInvalidLengthTx
+		}
+		if depth == 0 {
+			return iNdEx, nil
+		}
+	}
+	return 0, io.ErrUnexpectedEOF
+}
+
+var (
+	ErrInvalidLengthTx        = fmt.Errorf("proto: negative length found during unmarshaling")
+	ErrIntOverflowTx          = fmt.Errorf("proto: integer overflow")
+	ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group")
+)
diff --git a/x/virtualgroup/types/types.go b/x/virtualgroup/types/types.go
new file mode 100644
index 000000000..8f84fc5aa
--- /dev/null
+++ b/x/virtualgroup/types/types.go
@@ -0,0 +1,65 @@
+package types
+
+import (
+	"fmt"
+
+	sdkmath "cosmossdk.io/math"
+)
+
+type (
+	Int  = sdkmath.Int
+	Uint = sdkmath.Uint
+)
+
+func (f *GlobalVirtualGroupFamily) AppendGVG(gvgID uint32) {
+	f.GlobalVirtualGroupIds = append(f.GlobalVirtualGroupIds, gvgID)
+}
+
+func (f *GlobalVirtualGroupFamily) Contains(gvgID uint32) bool {
+	for _, id := range f.GlobalVirtualGroupIds {
+		if id == gvgID {
+			return true
+		}
+	}
+	return false
+}
+
+func (f *GlobalVirtualGroupFamily) RemoveGVG(gvgID uint32) error {
+	for i, id := range f.GlobalVirtualGroupIds {
+		if id == gvgID {
+			f.GlobalVirtualGroupIds = append(f.GlobalVirtualGroupIds[:i], f.GlobalVirtualGroupIds[i+1:]...)
+			return nil
+		}
+	}
+	return ErrGVGNotExist
+}
+
+func (f *GlobalVirtualGroupFamily) MustRemoveGVG(gvgID uint32) {
+	err := f.RemoveGVG(gvgID)
+	if err != nil {
+		panic(fmt.Sprintf("remove gvg from family failed. err: %s", err))
+	}
+}
+
+func (g *GlobalVirtualGroupsBindingOnBucket) AppendGVGAndLVG(gvgID, lvgID uint32) {
+	g.GlobalVirtualGroupIds = append(g.GlobalVirtualGroupIds, gvgID)
+	g.LocalVirtualGroupIds = append(g.LocalVirtualGroupIds, lvgID)
+}
+
+func (g *GlobalVirtualGroupsBindingOnBucket) GetLVGIDByGVGID(gvgID uint32) uint32 {
+	for i, gID := range g.GlobalVirtualGroupIds {
+		if gID == gvgID {
+			return g.LocalVirtualGroupIds[i]
+		}
+	}
+	return 0
+}
+
+func (g *GlobalVirtualGroupsBindingOnBucket) GetGVGIDByLVGID(lvgID uint32) uint32 {
+	for i, lID := range g.LocalVirtualGroupIds {
+		if lID == lvgID {
+			return g.GlobalVirtualGroupIds[i]
+		}
+	}
+	return 0
+}
diff --git a/x/virtualgroup/types/types.pb.go b/x/virtualgroup/types/types.pb.go
new file mode 100644
index 000000000..9cb8f886c
--- /dev/null
+++ b/x/virtualgroup/types/types.pb.go
@@ -0,0 +1,1742 @@
+// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// source: greenfield/virtualgroup/types.proto
+
+package types
+
+import (
+	fmt "fmt"
+	_ "github.com/cosmos/cosmos-proto"
+	github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types"
+	_ "github.com/cosmos/gogoproto/gogoproto"
+	proto "github.com/cosmos/gogoproto/proto"
+	io "io"
+	math "math"
+	math_bits "math/bits"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
+
+// A global virtual group consists of one primary SP (SP) and multiple secondary SP.
+// Every global virtual group must belong to a GVG family, and the objects of each
+// bucket must be stored in a GVG within a group family.
+type GlobalVirtualGroup struct {
+	// ID represents the unique identifier of the global virtual group.
+	Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
+	// Family ID represents the identifier of the GVG family that the group belongs to.
+	FamilyId uint32 `protobuf:"varint,2,opt,name=family_id,json=familyId,proto3" json:"family_id,omitempty"`
+	// Primary SP ID represents the unique identifier of the primary storage provider in the group.
+	PrimarySpId uint32 `protobuf:"varint,3,opt,name=primary_sp_id,json=primarySpId,proto3" json:"primary_sp_id,omitempty"`
+	// Secondary SP IDs represents the list of unique identifiers of the secondary storage providers in the group.
+	SecondarySpIds []uint32 `protobuf:"varint,4,rep,packed,name=secondary_sp_ids,json=secondarySpIds,proto3" json:"secondary_sp_ids,omitempty"`
+	// Stored size represents the size of the stored objects within the group.
+	StoredSize uint64 `protobuf:"varint,5,opt,name=stored_size,json=storedSize,proto3" json:"stored_size,omitempty"`
+	// Virtual payment address represents the payment address associated with the group.
+	VirtualPaymentAddress string `protobuf:"bytes,6,opt,name=virtual_payment_address,json=virtualPaymentAddress,proto3" json:"virtual_payment_address,omitempty"`
+	// Total deposit represents the number of tokens deposited by this storage provider for staking.
+	TotalDeposit github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,7,opt,name=total_deposit,json=totalDeposit,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"total_deposit"`
+}
+
+func (m *GlobalVirtualGroup) Reset()         { *m = GlobalVirtualGroup{} }
+func (m *GlobalVirtualGroup) String() string { return proto.CompactTextString(m) }
+func (*GlobalVirtualGroup) ProtoMessage()    {}
+func (*GlobalVirtualGroup) Descriptor() ([]byte, []int) {
+	return fileDescriptor_1fe6fc664532d0c3, []int{0}
+}
+func (m *GlobalVirtualGroup) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *GlobalVirtualGroup) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_GlobalVirtualGroup.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *GlobalVirtualGroup) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_GlobalVirtualGroup.Merge(m, src)
+}
+func (m *GlobalVirtualGroup) XXX_Size() int {
+	return m.Size()
+}
+func (m *GlobalVirtualGroup) XXX_DiscardUnknown() {
+	xxx_messageInfo_GlobalVirtualGroup.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_GlobalVirtualGroup proto.InternalMessageInfo
+
+func (m *GlobalVirtualGroup) GetId() uint32 {
+	if m != nil {
+		return m.Id
+	}
+	return 0
+}
+
+func (m *GlobalVirtualGroup) GetFamilyId() uint32 {
+	if m != nil {
+		return m.FamilyId
+	}
+	return 0
+}
+
+func (m *GlobalVirtualGroup) GetPrimarySpId() uint32 {
+	if m != nil {
+		return m.PrimarySpId
+	}
+	return 0
+}
+
+func (m *GlobalVirtualGroup) GetSecondarySpIds() []uint32 {
+	if m != nil {
+		return m.SecondarySpIds
+	}
+	return nil
+}
+
+func (m *GlobalVirtualGroup) GetStoredSize() uint64 {
+	if m != nil {
+		return m.StoredSize
+	}
+	return 0
+}
+
+func (m *GlobalVirtualGroup) GetVirtualPaymentAddress() string {
+	if m != nil {
+		return m.VirtualPaymentAddress
+	}
+	return ""
+}
+
+// Global virtual group family serve as a means of grouping global virtual groups.
+// Each bucket must be associated with a unique global virtual group family and cannot cross families.
+type GlobalVirtualGroupFamily struct {
+	// id is the identifier of the global virtual group family.
+	Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
+	// global_virtual_group_ids is a list of identifiers of the global virtual groups associated with the family.
+	GlobalVirtualGroupIds []uint32 `protobuf:"varint,2,rep,packed,name=global_virtual_group_ids,json=globalVirtualGroupIds,proto3" json:"global_virtual_group_ids,omitempty"`
+	// virtual_payment_address is the payment address associated with the global virtual group family.
+	VirtualPaymentAddress string `protobuf:"bytes,3,opt,name=virtual_payment_address,json=virtualPaymentAddress,proto3" json:"virtual_payment_address,omitempty"`
+}
+
+func (m *GlobalVirtualGroupFamily) Reset()         { *m = GlobalVirtualGroupFamily{} }
+func (m *GlobalVirtualGroupFamily) String() string { return proto.CompactTextString(m) }
+func (*GlobalVirtualGroupFamily) ProtoMessage()    {}
+func (*GlobalVirtualGroupFamily) Descriptor() ([]byte, []int) {
+	return fileDescriptor_1fe6fc664532d0c3, []int{1}
+}
+func (m *GlobalVirtualGroupFamily) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *GlobalVirtualGroupFamily) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_GlobalVirtualGroupFamily.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *GlobalVirtualGroupFamily) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_GlobalVirtualGroupFamily.Merge(m, src)
+}
+func (m *GlobalVirtualGroupFamily) XXX_Size() int {
+	return m.Size()
+}
+func (m *GlobalVirtualGroupFamily) XXX_DiscardUnknown() {
+	xxx_messageInfo_GlobalVirtualGroupFamily.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_GlobalVirtualGroupFamily proto.InternalMessageInfo
+
+func (m *GlobalVirtualGroupFamily) GetId() uint32 {
+	if m != nil {
+		return m.Id
+	}
+	return 0
+}
+
+func (m *GlobalVirtualGroupFamily) GetGlobalVirtualGroupIds() []uint32 {
+	if m != nil {
+		return m.GlobalVirtualGroupIds
+	}
+	return nil
+}
+
+func (m *GlobalVirtualGroupFamily) GetVirtualPaymentAddress() string {
+	if m != nil {
+		return m.VirtualPaymentAddress
+	}
+	return ""
+}
+
+type GlobalVirtualGroupsBindingOnBucket struct {
+	// bucket_id is the unique identification for the bucket.
+	BucketId Uint `protobuf:"bytes,1,opt,name=bucket_id,json=bucketId,proto3,customtype=Uint" json:"bucket_id"`
+	// global_virtual_group_ids is a list of identifiers of the global virtual groups associated with the bucket.
+	GlobalVirtualGroupIds []uint32 `protobuf:"varint,2,rep,packed,name=global_virtual_group_ids,json=globalVirtualGroupIds,proto3" json:"global_virtual_group_ids,omitempty"`
+	// local_virtual_group_ids is a list of identifiers of the local virtual groups associated with the bucket.
+	LocalVirtualGroupIds []uint32 `protobuf:"varint,3,rep,packed,name=local_virtual_group_ids,json=localVirtualGroupIds,proto3" json:"local_virtual_group_ids,omitempty"`
+}
+
+func (m *GlobalVirtualGroupsBindingOnBucket) Reset()         { *m = GlobalVirtualGroupsBindingOnBucket{} }
+func (m *GlobalVirtualGroupsBindingOnBucket) String() string { return proto.CompactTextString(m) }
+func (*GlobalVirtualGroupsBindingOnBucket) ProtoMessage()    {}
+func (*GlobalVirtualGroupsBindingOnBucket) Descriptor() ([]byte, []int) {
+	return fileDescriptor_1fe6fc664532d0c3, []int{2}
+}
+func (m *GlobalVirtualGroupsBindingOnBucket) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *GlobalVirtualGroupsBindingOnBucket) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_GlobalVirtualGroupsBindingOnBucket.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *GlobalVirtualGroupsBindingOnBucket) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_GlobalVirtualGroupsBindingOnBucket.Merge(m, src)
+}
+func (m *GlobalVirtualGroupsBindingOnBucket) XXX_Size() int {
+	return m.Size()
+}
+func (m *GlobalVirtualGroupsBindingOnBucket) XXX_DiscardUnknown() {
+	xxx_messageInfo_GlobalVirtualGroupsBindingOnBucket.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_GlobalVirtualGroupsBindingOnBucket proto.InternalMessageInfo
+
+func (m *GlobalVirtualGroupsBindingOnBucket) GetGlobalVirtualGroupIds() []uint32 {
+	if m != nil {
+		return m.GlobalVirtualGroupIds
+	}
+	return nil
+}
+
+func (m *GlobalVirtualGroupsBindingOnBucket) GetLocalVirtualGroupIds() []uint32 {
+	if m != nil {
+		return m.LocalVirtualGroupIds
+	}
+	return nil
+}
+
+type GVGStatisticsWithinSP struct {
+	// storage_provider_id defines the id of the sp which the statistics associated to
+	StorageProviderId uint32 `protobuf:"varint,1,opt,name=storage_provider_id,json=storageProviderId,proto3" json:"storage_provider_id,omitempty"`
+	// secondary_count defines the number of global virtual groups (GVGs) in
+	// which this storage provider serves as a secondary storage provider.
+	SecondaryCount uint32 `protobuf:"varint,3,opt,name=secondary_count,json=secondaryCount,proto3" json:"secondary_count,omitempty"`
+}
+
+func (m *GVGStatisticsWithinSP) Reset()         { *m = GVGStatisticsWithinSP{} }
+func (m *GVGStatisticsWithinSP) String() string { return proto.CompactTextString(m) }
+func (*GVGStatisticsWithinSP) ProtoMessage()    {}
+func (*GVGStatisticsWithinSP) Descriptor() ([]byte, []int) {
+	return fileDescriptor_1fe6fc664532d0c3, []int{3}
+}
+func (m *GVGStatisticsWithinSP) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *GVGStatisticsWithinSP) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_GVGStatisticsWithinSP.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *GVGStatisticsWithinSP) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_GVGStatisticsWithinSP.Merge(m, src)
+}
+func (m *GVGStatisticsWithinSP) XXX_Size() int {
+	return m.Size()
+}
+func (m *GVGStatisticsWithinSP) XXX_DiscardUnknown() {
+	xxx_messageInfo_GVGStatisticsWithinSP.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_GVGStatisticsWithinSP proto.InternalMessageInfo
+
+func (m *GVGStatisticsWithinSP) GetStorageProviderId() uint32 {
+	if m != nil {
+		return m.StorageProviderId
+	}
+	return 0
+}
+
+func (m *GVGStatisticsWithinSP) GetSecondaryCount() uint32 {
+	if m != nil {
+		return m.SecondaryCount
+	}
+	return 0
+}
+
+type SwapOutInfo struct {
+	// sp_id is the unique id of the storage provider who want to swap out.
+	SpId uint32 `protobuf:"varint,1,opt,name=sp_id,json=spId,proto3" json:"sp_id,omitempty"`
+	// successor_sp_id is the id of the successor storage provider.
+	SuccessorSpId uint32 `protobuf:"varint,2,opt,name=successor_sp_id,json=successorSpId,proto3" json:"successor_sp_id,omitempty"`
+}
+
+func (m *SwapOutInfo) Reset()         { *m = SwapOutInfo{} }
+func (m *SwapOutInfo) String() string { return proto.CompactTextString(m) }
+func (*SwapOutInfo) ProtoMessage()    {}
+func (*SwapOutInfo) Descriptor() ([]byte, []int) {
+	return fileDescriptor_1fe6fc664532d0c3, []int{4}
+}
+func (m *SwapOutInfo) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *SwapOutInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_SwapOutInfo.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *SwapOutInfo) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_SwapOutInfo.Merge(m, src)
+}
+func (m *SwapOutInfo) XXX_Size() int {
+	return m.Size()
+}
+func (m *SwapOutInfo) XXX_DiscardUnknown() {
+	xxx_messageInfo_SwapOutInfo.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_SwapOutInfo proto.InternalMessageInfo
+
+func (m *SwapOutInfo) GetSpId() uint32 {
+	if m != nil {
+		return m.SpId
+	}
+	return 0
+}
+
+func (m *SwapOutInfo) GetSuccessorSpId() uint32 {
+	if m != nil {
+		return m.SuccessorSpId
+	}
+	return 0
+}
+
+func init() {
+	proto.RegisterType((*GlobalVirtualGroup)(nil), "greenfield.virtualgroup.GlobalVirtualGroup")
+	proto.RegisterType((*GlobalVirtualGroupFamily)(nil), "greenfield.virtualgroup.GlobalVirtualGroupFamily")
+	proto.RegisterType((*GlobalVirtualGroupsBindingOnBucket)(nil), "greenfield.virtualgroup.GlobalVirtualGroupsBindingOnBucket")
+	proto.RegisterType((*GVGStatisticsWithinSP)(nil), "greenfield.virtualgroup.GVGStatisticsWithinSP")
+	proto.RegisterType((*SwapOutInfo)(nil), "greenfield.virtualgroup.SwapOutInfo")
+}
+
+func init() {
+	proto.RegisterFile("greenfield/virtualgroup/types.proto", fileDescriptor_1fe6fc664532d0c3)
+}
+
+var fileDescriptor_1fe6fc664532d0c3 = []byte{
+	// 608 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x54, 0x41, 0x6f, 0xd3, 0x3c,
+	0x18, 0x6e, 0xda, 0x6e, 0xdf, 0xea, 0x7e, 0xdd, 0xc0, 0xdb, 0xb4, 0xb0, 0x49, 0x59, 0x15, 0xa4,
+	0xd1, 0x4b, 0xdb, 0x03, 0x20, 0x38, 0x70, 0xa1, 0x20, 0xaa, 0x70, 0x60, 0x55, 0x22, 0x86, 0xc4,
+	0x25, 0x72, 0x62, 0x2f, 0xb3, 0x96, 0xda, 0x51, 0xec, 0x0c, 0xba, 0x5f, 0xc1, 0x8f, 0x19, 0xff,
+	0x61, 0x27, 0x34, 0xed, 0x84, 0x38, 0x4c, 0x68, 0x13, 0xff, 0x03, 0xc5, 0x36, 0x6d, 0x45, 0x27,
+	0x0e, 0x70, 0x8a, 0xfd, 0xbc, 0xcf, 0xfb, 0x38, 0xef, 0xe3, 0xf7, 0x35, 0xb8, 0x9f, 0xe4, 0x84,
+	0xb0, 0x43, 0x4a, 0x52, 0xdc, 0x3f, 0xa1, 0xb9, 0x2c, 0x50, 0x9a, 0xe4, 0xbc, 0xc8, 0xfa, 0x72,
+	0x92, 0x11, 0xd1, 0xcb, 0x72, 0x2e, 0x39, 0xdc, 0x9a, 0x91, 0x7a, 0xf3, 0xa4, 0xed, 0x7b, 0x31,
+	0x17, 0x63, 0x2e, 0x42, 0x45, 0xeb, 0xeb, 0x8d, 0xce, 0xd9, 0xde, 0x48, 0x78, 0xc2, 0x35, 0x5e,
+	0xae, 0x34, 0xea, 0xfe, 0xa8, 0x02, 0x38, 0x4c, 0x79, 0x84, 0xd2, 0x03, 0xad, 0x33, 0x2c, 0x75,
+	0xe0, 0x2a, 0xa8, 0x52, 0x6c, 0x5b, 0x6d, 0xab, 0xd3, 0xf2, 0xab, 0x14, 0xc3, 0x1d, 0xd0, 0x38,
+	0x44, 0x63, 0x9a, 0x4e, 0x42, 0x8a, 0xed, 0xaa, 0x82, 0x57, 0x34, 0xe0, 0x61, 0xe8, 0x82, 0x56,
+	0x96, 0xd3, 0x31, 0xca, 0x27, 0xa1, 0xc8, 0x4a, 0x42, 0x4d, 0x11, 0x9a, 0x06, 0x0c, 0x32, 0x0f,
+	0xc3, 0x0e, 0xb8, 0x23, 0x48, 0xcc, 0x19, 0x9e, 0xb2, 0x84, 0x5d, 0x6f, 0xd7, 0x3a, 0x2d, 0x7f,
+	0x75, 0x8a, 0x97, 0x44, 0x01, 0x77, 0x41, 0x53, 0x48, 0x9e, 0x13, 0x1c, 0x0a, 0x7a, 0x4a, 0xec,
+	0xa5, 0xb6, 0xd5, 0xa9, 0xfb, 0x40, 0x43, 0x01, 0x3d, 0x25, 0x70, 0x04, 0xb6, 0x4c, 0xcd, 0x61,
+	0x86, 0x26, 0x63, 0xc2, 0x64, 0x88, 0x30, 0xce, 0x89, 0x10, 0xf6, 0x72, 0xdb, 0xea, 0x34, 0x06,
+	0xf6, 0xe5, 0x59, 0x77, 0xc3, 0xd4, 0xfe, 0x5c, 0x47, 0x02, 0x99, 0x53, 0x96, 0xf8, 0x9b, 0x26,
+	0x71, 0xa4, 0xf3, 0x4c, 0x10, 0x22, 0xd0, 0x92, 0x5c, 0xa2, 0x34, 0xc4, 0x24, 0xe3, 0x82, 0x4a,
+	0xfb, 0x3f, 0xa5, 0xf3, 0xec, 0xfc, 0x6a, 0xb7, 0xf2, 0xed, 0x6a, 0x77, 0x2f, 0xa1, 0xf2, 0xa8,
+	0x88, 0x7a, 0x31, 0x1f, 0x1b, 0x4b, 0xcd, 0xa7, 0x2b, 0xf0, 0xb1, 0xb9, 0x17, 0x8f, 0xc9, 0xcb,
+	0xb3, 0x2e, 0x30, 0xa7, 0x7a, 0x4c, 0xfa, 0xff, 0x2b, 0xc9, 0x97, 0x5a, 0xd1, 0xfd, 0x6c, 0x01,
+	0x7b, 0xd1, 0xe7, 0x57, 0xca, 0xc2, 0x05, 0xb7, 0x9f, 0x00, 0x3b, 0x51, 0xdc, 0xf0, 0x57, 0xa1,
+	0xea, 0x76, 0x95, 0x69, 0x55, 0x65, 0xda, 0x66, 0xb2, 0xa0, 0x55, 0x7a, 0xf7, 0x07, 0x6b, 0x6a,
+	0x7f, 0x65, 0x8d, 0xfb, 0xc5, 0x02, 0xee, 0xe2, 0x7f, 0x8b, 0x01, 0x65, 0x98, 0xb2, 0x64, 0x9f,
+	0x0d, 0x8a, 0xf8, 0x98, 0x48, 0xf8, 0x14, 0x34, 0x22, 0xb5, 0x0a, 0x4d, 0x21, 0x8d, 0xc1, 0x8e,
+	0x71, 0xaf, 0xfe, 0x96, 0x2a, 0x6f, 0x9a, 0xe6, 0xd8, 0x72, 0xeb, 0xaf, 0x68, 0xb6, 0xf7, 0x0f,
+	0xb5, 0x3e, 0x06, 0x5b, 0x29, 0x8f, 0x6f, 0xcd, 0xab, 0xa9, 0xbc, 0x0d, 0x15, 0xfe, 0x2d, 0xcd,
+	0xcd, 0xc0, 0xe6, 0xf0, 0x60, 0x18, 0x48, 0x24, 0xa9, 0x90, 0x34, 0x16, 0xef, 0xa8, 0x3c, 0xa2,
+	0x2c, 0x18, 0xc1, 0x1e, 0x58, 0x2f, 0x9b, 0x0c, 0x25, 0xa4, 0x9c, 0x9e, 0x13, 0x8a, 0x49, 0x1e,
+	0x4e, 0x6f, 0xe5, 0xae, 0x09, 0x8d, 0x4c, 0xc4, 0xc3, 0xf0, 0x01, 0x58, 0x9b, 0x75, 0x74, 0xcc,
+	0x0b, 0x26, 0x4d, 0xdf, 0xcf, 0x1a, 0xfa, 0x45, 0x89, 0xba, 0xaf, 0x41, 0x33, 0xf8, 0x80, 0xb2,
+	0xfd, 0x42, 0x7a, 0xec, 0x90, 0xc3, 0x75, 0xb0, 0xa4, 0xa7, 0x44, 0x2b, 0xd7, 0x45, 0x39, 0x1e,
+	0x7b, 0x60, 0x4d, 0x14, 0x71, 0x4c, 0x84, 0xe0, 0xb9, 0x19, 0x22, 0x3d, 0x65, 0xad, 0x29, 0x5c,
+	0x4e, 0xc7, 0xe0, 0xcd, 0xf9, 0xb5, 0x63, 0x5d, 0x5c, 0x3b, 0xd6, 0xf7, 0x6b, 0xc7, 0xfa, 0x74,
+	0xe3, 0x54, 0x2e, 0x6e, 0x9c, 0xca, 0xd7, 0x1b, 0xa7, 0xf2, 0xfe, 0xd1, 0x5c, 0x93, 0x46, 0x2c,
+	0xea, 0xc6, 0x47, 0x88, 0xb2, 0xfe, 0xdc, 0x63, 0xf2, 0xf1, 0x96, 0xe7, 0x24, 0x5a, 0x56, 0xaf,
+	0xc0, 0xc3, 0x9f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xc9, 0x5c, 0xa3, 0x7d, 0x76, 0x04, 0x00, 0x00,
+}
+
+func (m *GlobalVirtualGroup) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *GlobalVirtualGroup) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *GlobalVirtualGroup) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	{
+		size := m.TotalDeposit.Size()
+		i -= size
+		if _, err := m.TotalDeposit.MarshalTo(dAtA[i:]); err != nil {
+			return 0, err
+		}
+		i = encodeVarintTypes(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0x3a
+	if len(m.VirtualPaymentAddress) > 0 {
+		i -= len(m.VirtualPaymentAddress)
+		copy(dAtA[i:], m.VirtualPaymentAddress)
+		i = encodeVarintTypes(dAtA, i, uint64(len(m.VirtualPaymentAddress)))
+		i--
+		dAtA[i] = 0x32
+	}
+	if m.StoredSize != 0 {
+		i = encodeVarintTypes(dAtA, i, uint64(m.StoredSize))
+		i--
+		dAtA[i] = 0x28
+	}
+	if len(m.SecondarySpIds) > 0 {
+		dAtA2 := make([]byte, len(m.SecondarySpIds)*10)
+		var j1 int
+		for _, num := range m.SecondarySpIds {
+			for num >= 1<<7 {
+				dAtA2[j1] = uint8(uint64(num)&0x7f | 0x80)
+				num >>= 7
+				j1++
+			}
+			dAtA2[j1] = uint8(num)
+			j1++
+		}
+		i -= j1
+		copy(dAtA[i:], dAtA2[:j1])
+		i = encodeVarintTypes(dAtA, i, uint64(j1))
+		i--
+		dAtA[i] = 0x22
+	}
+	if m.PrimarySpId != 0 {
+		i = encodeVarintTypes(dAtA, i, uint64(m.PrimarySpId))
+		i--
+		dAtA[i] = 0x18
+	}
+	if m.FamilyId != 0 {
+		i = encodeVarintTypes(dAtA, i, uint64(m.FamilyId))
+		i--
+		dAtA[i] = 0x10
+	}
+	if m.Id != 0 {
+		i = encodeVarintTypes(dAtA, i, uint64(m.Id))
+		i--
+		dAtA[i] = 0x8
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *GlobalVirtualGroupFamily) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *GlobalVirtualGroupFamily) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *GlobalVirtualGroupFamily) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.VirtualPaymentAddress) > 0 {
+		i -= len(m.VirtualPaymentAddress)
+		copy(dAtA[i:], m.VirtualPaymentAddress)
+		i = encodeVarintTypes(dAtA, i, uint64(len(m.VirtualPaymentAddress)))
+		i--
+		dAtA[i] = 0x1a
+	}
+	if len(m.GlobalVirtualGroupIds) > 0 {
+		dAtA4 := make([]byte, len(m.GlobalVirtualGroupIds)*10)
+		var j3 int
+		for _, num := range m.GlobalVirtualGroupIds {
+			for num >= 1<<7 {
+				dAtA4[j3] = uint8(uint64(num)&0x7f | 0x80)
+				num >>= 7
+				j3++
+			}
+			dAtA4[j3] = uint8(num)
+			j3++
+		}
+		i -= j3
+		copy(dAtA[i:], dAtA4[:j3])
+		i = encodeVarintTypes(dAtA, i, uint64(j3))
+		i--
+		dAtA[i] = 0x12
+	}
+	if m.Id != 0 {
+		i = encodeVarintTypes(dAtA, i, uint64(m.Id))
+		i--
+		dAtA[i] = 0x8
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *GlobalVirtualGroupsBindingOnBucket) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *GlobalVirtualGroupsBindingOnBucket) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *GlobalVirtualGroupsBindingOnBucket) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.LocalVirtualGroupIds) > 0 {
+		dAtA6 := make([]byte, len(m.LocalVirtualGroupIds)*10)
+		var j5 int
+		for _, num := range m.LocalVirtualGroupIds {
+			for num >= 1<<7 {
+				dAtA6[j5] = uint8(uint64(num)&0x7f | 0x80)
+				num >>= 7
+				j5++
+			}
+			dAtA6[j5] = uint8(num)
+			j5++
+		}
+		i -= j5
+		copy(dAtA[i:], dAtA6[:j5])
+		i = encodeVarintTypes(dAtA, i, uint64(j5))
+		i--
+		dAtA[i] = 0x1a
+	}
+	if len(m.GlobalVirtualGroupIds) > 0 {
+		dAtA8 := make([]byte, len(m.GlobalVirtualGroupIds)*10)
+		var j7 int
+		for _, num := range m.GlobalVirtualGroupIds {
+			for num >= 1<<7 {
+				dAtA8[j7] = uint8(uint64(num)&0x7f | 0x80)
+				num >>= 7
+				j7++
+			}
+			dAtA8[j7] = uint8(num)
+			j7++
+		}
+		i -= j7
+		copy(dAtA[i:], dAtA8[:j7])
+		i = encodeVarintTypes(dAtA, i, uint64(j7))
+		i--
+		dAtA[i] = 0x12
+	}
+	{
+		size := m.BucketId.Size()
+		i -= size
+		if _, err := m.BucketId.MarshalTo(dAtA[i:]); err != nil {
+			return 0, err
+		}
+		i = encodeVarintTypes(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0xa
+	return len(dAtA) - i, nil
+}
+
+func (m *GVGStatisticsWithinSP) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *GVGStatisticsWithinSP) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *GVGStatisticsWithinSP) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.SecondaryCount != 0 {
+		i = encodeVarintTypes(dAtA, i, uint64(m.SecondaryCount))
+		i--
+		dAtA[i] = 0x18
+	}
+	if m.StorageProviderId != 0 {
+		i = encodeVarintTypes(dAtA, i, uint64(m.StorageProviderId))
+		i--
+		dAtA[i] = 0x8
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *SwapOutInfo) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *SwapOutInfo) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *SwapOutInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.SuccessorSpId != 0 {
+		i = encodeVarintTypes(dAtA, i, uint64(m.SuccessorSpId))
+		i--
+		dAtA[i] = 0x10
+	}
+	if m.SpId != 0 {
+		i = encodeVarintTypes(dAtA, i, uint64(m.SpId))
+		i--
+		dAtA[i] = 0x8
+	}
+	return len(dAtA) - i, nil
+}
+
+func encodeVarintTypes(dAtA []byte, offset int, v uint64) int {
+	offset -= sovTypes(v)
+	base := offset
+	for v >= 1<<7 {
+		dAtA[offset] = uint8(v&0x7f | 0x80)
+		v >>= 7
+		offset++
+	}
+	dAtA[offset] = uint8(v)
+	return base
+}
+func (m *GlobalVirtualGroup) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.Id != 0 {
+		n += 1 + sovTypes(uint64(m.Id))
+	}
+	if m.FamilyId != 0 {
+		n += 1 + sovTypes(uint64(m.FamilyId))
+	}
+	if m.PrimarySpId != 0 {
+		n += 1 + sovTypes(uint64(m.PrimarySpId))
+	}
+	if len(m.SecondarySpIds) > 0 {
+		l = 0
+		for _, e := range m.SecondarySpIds {
+			l += sovTypes(uint64(e))
+		}
+		n += 1 + sovTypes(uint64(l)) + l
+	}
+	if m.StoredSize != 0 {
+		n += 1 + sovTypes(uint64(m.StoredSize))
+	}
+	l = len(m.VirtualPaymentAddress)
+	if l > 0 {
+		n += 1 + l + sovTypes(uint64(l))
+	}
+	l = m.TotalDeposit.Size()
+	n += 1 + l + sovTypes(uint64(l))
+	return n
+}
+
+func (m *GlobalVirtualGroupFamily) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.Id != 0 {
+		n += 1 + sovTypes(uint64(m.Id))
+	}
+	if len(m.GlobalVirtualGroupIds) > 0 {
+		l = 0
+		for _, e := range m.GlobalVirtualGroupIds {
+			l += sovTypes(uint64(e))
+		}
+		n += 1 + sovTypes(uint64(l)) + l
+	}
+	l = len(m.VirtualPaymentAddress)
+	if l > 0 {
+		n += 1 + l + sovTypes(uint64(l))
+	}
+	return n
+}
+
+func (m *GlobalVirtualGroupsBindingOnBucket) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = m.BucketId.Size()
+	n += 1 + l + sovTypes(uint64(l))
+	if len(m.GlobalVirtualGroupIds) > 0 {
+		l = 0
+		for _, e := range m.GlobalVirtualGroupIds {
+			l += sovTypes(uint64(e))
+		}
+		n += 1 + sovTypes(uint64(l)) + l
+	}
+	if len(m.LocalVirtualGroupIds) > 0 {
+		l = 0
+		for _, e := range m.LocalVirtualGroupIds {
+			l += sovTypes(uint64(e))
+		}
+		n += 1 + sovTypes(uint64(l)) + l
+	}
+	return n
+}
+
+func (m *GVGStatisticsWithinSP) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.StorageProviderId != 0 {
+		n += 1 + sovTypes(uint64(m.StorageProviderId))
+	}
+	if m.SecondaryCount != 0 {
+		n += 1 + sovTypes(uint64(m.SecondaryCount))
+	}
+	return n
+}
+
+func (m *SwapOutInfo) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.SpId != 0 {
+		n += 1 + sovTypes(uint64(m.SpId))
+	}
+	if m.SuccessorSpId != 0 {
+		n += 1 + sovTypes(uint64(m.SuccessorSpId))
+	}
+	return n
+}
+
+func sovTypes(x uint64) (n int) {
+	return (math_bits.Len64(x|1) + 6) / 7
+}
+func sozTypes(x uint64) (n int) {
+	return sovTypes(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *GlobalVirtualGroup) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTypes
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: GlobalVirtualGroup: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: GlobalVirtualGroup: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType)
+			}
+			m.Id = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTypes
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.Id |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field FamilyId", wireType)
+			}
+			m.FamilyId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTypes
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.FamilyId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 3:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field PrimarySpId", wireType)
+			}
+			m.PrimarySpId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTypes
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.PrimarySpId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 4:
+			if wireType == 0 {
+				var v uint32
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowTypes
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					v |= uint32(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				m.SecondarySpIds = append(m.SecondarySpIds, v)
+			} else if wireType == 2 {
+				var packedLen int
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowTypes
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					packedLen |= int(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				if packedLen < 0 {
+					return ErrInvalidLengthTypes
+				}
+				postIndex := iNdEx + packedLen
+				if postIndex < 0 {
+					return ErrInvalidLengthTypes
+				}
+				if postIndex > l {
+					return io.ErrUnexpectedEOF
+				}
+				var elementCount int
+				var count int
+				for _, integer := range dAtA[iNdEx:postIndex] {
+					if integer < 128 {
+						count++
+					}
+				}
+				elementCount = count
+				if elementCount != 0 && len(m.SecondarySpIds) == 0 {
+					m.SecondarySpIds = make([]uint32, 0, elementCount)
+				}
+				for iNdEx < postIndex {
+					var v uint32
+					for shift := uint(0); ; shift += 7 {
+						if shift >= 64 {
+							return ErrIntOverflowTypes
+						}
+						if iNdEx >= l {
+							return io.ErrUnexpectedEOF
+						}
+						b := dAtA[iNdEx]
+						iNdEx++
+						v |= uint32(b&0x7F) << shift
+						if b < 0x80 {
+							break
+						}
+					}
+					m.SecondarySpIds = append(m.SecondarySpIds, v)
+				}
+			} else {
+				return fmt.Errorf("proto: wrong wireType = %d for field SecondarySpIds", wireType)
+			}
+		case 5:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field StoredSize", wireType)
+			}
+			m.StoredSize = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTypes
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.StoredSize |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 6:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field VirtualPaymentAddress", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTypes
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTypes
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTypes
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.VirtualPaymentAddress = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 7:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field TotalDeposit", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTypes
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTypes
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTypes
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.TotalDeposit.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTypes(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTypes
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *GlobalVirtualGroupFamily) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTypes
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: GlobalVirtualGroupFamily: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: GlobalVirtualGroupFamily: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType)
+			}
+			m.Id = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTypes
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.Id |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType == 0 {
+				var v uint32
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowTypes
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					v |= uint32(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				m.GlobalVirtualGroupIds = append(m.GlobalVirtualGroupIds, v)
+			} else if wireType == 2 {
+				var packedLen int
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowTypes
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					packedLen |= int(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				if packedLen < 0 {
+					return ErrInvalidLengthTypes
+				}
+				postIndex := iNdEx + packedLen
+				if postIndex < 0 {
+					return ErrInvalidLengthTypes
+				}
+				if postIndex > l {
+					return io.ErrUnexpectedEOF
+				}
+				var elementCount int
+				var count int
+				for _, integer := range dAtA[iNdEx:postIndex] {
+					if integer < 128 {
+						count++
+					}
+				}
+				elementCount = count
+				if elementCount != 0 && len(m.GlobalVirtualGroupIds) == 0 {
+					m.GlobalVirtualGroupIds = make([]uint32, 0, elementCount)
+				}
+				for iNdEx < postIndex {
+					var v uint32
+					for shift := uint(0); ; shift += 7 {
+						if shift >= 64 {
+							return ErrIntOverflowTypes
+						}
+						if iNdEx >= l {
+							return io.ErrUnexpectedEOF
+						}
+						b := dAtA[iNdEx]
+						iNdEx++
+						v |= uint32(b&0x7F) << shift
+						if b < 0x80 {
+							break
+						}
+					}
+					m.GlobalVirtualGroupIds = append(m.GlobalVirtualGroupIds, v)
+				}
+			} else {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupIds", wireType)
+			}
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field VirtualPaymentAddress", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTypes
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTypes
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTypes
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.VirtualPaymentAddress = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTypes(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTypes
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *GlobalVirtualGroupsBindingOnBucket) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTypes
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: GlobalVirtualGroupsBindingOnBucket: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: GlobalVirtualGroupsBindingOnBucket: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field BucketId", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTypes
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTypes
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTypes
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.BucketId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 2:
+			if wireType == 0 {
+				var v uint32
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowTypes
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					v |= uint32(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				m.GlobalVirtualGroupIds = append(m.GlobalVirtualGroupIds, v)
+			} else if wireType == 2 {
+				var packedLen int
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowTypes
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					packedLen |= int(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				if packedLen < 0 {
+					return ErrInvalidLengthTypes
+				}
+				postIndex := iNdEx + packedLen
+				if postIndex < 0 {
+					return ErrInvalidLengthTypes
+				}
+				if postIndex > l {
+					return io.ErrUnexpectedEOF
+				}
+				var elementCount int
+				var count int
+				for _, integer := range dAtA[iNdEx:postIndex] {
+					if integer < 128 {
+						count++
+					}
+				}
+				elementCount = count
+				if elementCount != 0 && len(m.GlobalVirtualGroupIds) == 0 {
+					m.GlobalVirtualGroupIds = make([]uint32, 0, elementCount)
+				}
+				for iNdEx < postIndex {
+					var v uint32
+					for shift := uint(0); ; shift += 7 {
+						if shift >= 64 {
+							return ErrIntOverflowTypes
+						}
+						if iNdEx >= l {
+							return io.ErrUnexpectedEOF
+						}
+						b := dAtA[iNdEx]
+						iNdEx++
+						v |= uint32(b&0x7F) << shift
+						if b < 0x80 {
+							break
+						}
+					}
+					m.GlobalVirtualGroupIds = append(m.GlobalVirtualGroupIds, v)
+				}
+			} else {
+				return fmt.Errorf("proto: wrong wireType = %d for field GlobalVirtualGroupIds", wireType)
+			}
+		case 3:
+			if wireType == 0 {
+				var v uint32
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowTypes
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					v |= uint32(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				m.LocalVirtualGroupIds = append(m.LocalVirtualGroupIds, v)
+			} else if wireType == 2 {
+				var packedLen int
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowTypes
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					packedLen |= int(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				if packedLen < 0 {
+					return ErrInvalidLengthTypes
+				}
+				postIndex := iNdEx + packedLen
+				if postIndex < 0 {
+					return ErrInvalidLengthTypes
+				}
+				if postIndex > l {
+					return io.ErrUnexpectedEOF
+				}
+				var elementCount int
+				var count int
+				for _, integer := range dAtA[iNdEx:postIndex] {
+					if integer < 128 {
+						count++
+					}
+				}
+				elementCount = count
+				if elementCount != 0 && len(m.LocalVirtualGroupIds) == 0 {
+					m.LocalVirtualGroupIds = make([]uint32, 0, elementCount)
+				}
+				for iNdEx < postIndex {
+					var v uint32
+					for shift := uint(0); ; shift += 7 {
+						if shift >= 64 {
+							return ErrIntOverflowTypes
+						}
+						if iNdEx >= l {
+							return io.ErrUnexpectedEOF
+						}
+						b := dAtA[iNdEx]
+						iNdEx++
+						v |= uint32(b&0x7F) << shift
+						if b < 0x80 {
+							break
+						}
+					}
+					m.LocalVirtualGroupIds = append(m.LocalVirtualGroupIds, v)
+				}
+			} else {
+				return fmt.Errorf("proto: wrong wireType = %d for field LocalVirtualGroupIds", wireType)
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTypes(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTypes
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *GVGStatisticsWithinSP) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTypes
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: GVGStatisticsWithinSP: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: GVGStatisticsWithinSP: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field StorageProviderId", wireType)
+			}
+			m.StorageProviderId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTypes
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.StorageProviderId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 3:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field SecondaryCount", wireType)
+			}
+			m.SecondaryCount = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTypes
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.SecondaryCount |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTypes(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTypes
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *SwapOutInfo) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTypes
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: SwapOutInfo: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: SwapOutInfo: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field SpId", wireType)
+			}
+			m.SpId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTypes
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.SpId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field SuccessorSpId", wireType)
+			}
+			m.SuccessorSpId = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTypes
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.SuccessorSpId |= uint32(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTypes(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTypes
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func skipTypes(dAtA []byte) (n int, err error) {
+	l := len(dAtA)
+	iNdEx := 0
+	depth := 0
+	for iNdEx < l {
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return 0, ErrIntOverflowTypes
+			}
+			if iNdEx >= l {
+				return 0, io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		wireType := int(wire & 0x7)
+		switch wireType {
+		case 0:
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowTypes
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				iNdEx++
+				if dAtA[iNdEx-1] < 0x80 {
+					break
+				}
+			}
+		case 1:
+			iNdEx += 8
+		case 2:
+			var length int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowTypes
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				length |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if length < 0 {
+				return 0, ErrInvalidLengthTypes
+			}
+			iNdEx += length
+		case 3:
+			depth++
+		case 4:
+			if depth == 0 {
+				return 0, ErrUnexpectedEndOfGroupTypes
+			}
+			depth--
+		case 5:
+			iNdEx += 4
+		default:
+			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+		}
+		if iNdEx < 0 {
+			return 0, ErrInvalidLengthTypes
+		}
+		if depth == 0 {
+			return iNdEx, nil
+		}
+	}
+	return 0, io.ErrUnexpectedEOF
+}
+
+var (
+	ErrInvalidLengthTypes        = fmt.Errorf("proto: negative length found during unmarshaling")
+	ErrIntOverflowTypes          = fmt.Errorf("proto: integer overflow")
+	ErrUnexpectedEndOfGroupTypes = fmt.Errorf("proto: unexpected end of group")
+)