Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(delegation): query maximum undelegation amount #285

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions client/docs/statik/statik.go

Large diffs are not rendered by default.

50 changes: 43 additions & 7 deletions client/docs/swagger-ui/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -20816,24 +20816,35 @@
},
"/exocore/delegation/v1/single_delegation/{staker_id}/{operator_addr}/{asset_id}": {
"get": {
"summary": "SingleDelegationInfo queries the single delegation information for\n{chain, staker, asset, operator}.",
"summary": "SingleDelegationInfo queries the single delegation information and the\nmaximum undelegatable amount for {staker, asset, operator}.",
"operationId": "QuerySingleDelegationInfo",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"type": "object",
"properties": {
"undelegatable_share": {
"type": "string",
"description": "undelegatable_share is the share that can be undelegated.\nIt's to reduce the state updating when slash occurs.\nS_j = S * T_j / T, `S` and `T` is the current asset share and amount of operator,\nand the T_j represents the change in staker's asset amount when some external\noperations occur, such as: delegation, undelegation and slashing.\nS_j represents the change in the staker's asset share,\nso the updated share should be added by it.\nA special case is the initial delegation, when T = 0 and S = 0, so T_j / T is undefined.\nFor the initial delegation, delegator j who delegates T_j tokens receive S_j = T_j shares."
"delegation_amounts": {
"type": "object",
"properties": {
"undelegatable_share": {
"type": "string",
"description": "undelegatable_share is the share that can be undelegated.\nIt's to reduce the state updating when slash occurs.\nS_j = S * T_j / T, `S` and `T` is the current asset share and amount of operator,\nand the T_j represents the change in staker's asset amount when some external\noperations occur, such as: delegation, undelegation and slashing.\nS_j represents the change in the staker's asset share,\nso the updated share should be added by it.\nA special case is the initial delegation, when T = 0 and S = 0, so T_j / T is undefined.\nFor the initial delegation, delegator j who delegates T_j tokens receive S_j = T_j shares."
},
"wait_undelegation_amount": {
"type": "string",
"description": "wait_undelegation_amount is the amount that is waiting to be unbonded."
}
},
"description": "DelegationAmounts is the delegation amount response for a single delegation.",
"title": "delegation_amounts is the delegation info recorded in the KVStore"
},
"wait_undelegation_amount": {
"max_undelegatable_amount": {
"type": "string",
"description": "wait_undelegation_amount is the amount that is waiting to be unbonded."
"title": "max_undelegatable_amount is the maximum amount that can be undelegated"
}
},
"description": "DelegationAmounts is the delegation amount response for a single delegation."
"title": "SingleDelegationInfoResponse is the response to QuerySingleDelegationInfo"
}
},
"default": {
Expand Down Expand Up @@ -43411,6 +43422,31 @@
},
"description": "QueryDelegationInfoResponse is the response for delegations by staker id and\nasset id."
},
"exocore.delegation.v1.SingleDelegationInfoResponse": {
"type": "object",
"properties": {
"delegation_amounts": {
"type": "object",
"properties": {
"undelegatable_share": {
"type": "string",
"description": "undelegatable_share is the share that can be undelegated.\nIt's to reduce the state updating when slash occurs.\nS_j = S * T_j / T, `S` and `T` is the current asset share and amount of operator,\nand the T_j represents the change in staker's asset amount when some external\noperations occur, such as: delegation, undelegation and slashing.\nS_j represents the change in the staker's asset share,\nso the updated share should be added by it.\nA special case is the initial delegation, when T = 0 and S = 0, so T_j / T is undefined.\nFor the initial delegation, delegator j who delegates T_j tokens receive S_j = T_j shares."
},
"wait_undelegation_amount": {
"type": "string",
"description": "wait_undelegation_amount is the amount that is waiting to be unbonded."
}
},
"description": "DelegationAmounts is the delegation amount response for a single delegation.",
"title": "delegation_amounts is the delegation info recorded in the KVStore"
},
"max_undelegatable_amount": {
"type": "string",
"title": "max_undelegatable_amount is the maximum amount that can be undelegated"
}
},
"title": "SingleDelegationInfoResponse is the response to QuerySingleDelegationInfo"
},
"exocore.delegation.v1.UndelegationAndHoldCount": {
"type": "object",
"properties": {
Expand Down
27 changes: 20 additions & 7 deletions proto/exocore/delegation/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@
map<string, DelegationAmounts> delegation_infos = 1;
}

// SingleDelegationInfoReq is the request to obtain the single delegation information.
// SingleDelegationInfoReq is the request to obtain the single delegation information
// and the maximum undelegatable amount of specific delegation.
message SingleDelegationInfoReq {
// staker_id is the staker id.
string staker_id = 1;
Expand All @@ -68,14 +69,26 @@
string asset_id = 3;
}

// SingleDelegationInfoResponse is the response to QuerySingleDelegationInfo
message SingleDelegationInfoResponse {
// delegation_amounts is the delegation info recorded in the KVStore
DelegationAmounts delegation_amounts = 1;
// max_undelegatable_amount is the maximum amount that can be undelegated
string max_undelegatable_amount = 2 [
(cosmos_proto.scalar) = "cosmos.Int",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
];
}

// UndelegationHoldCountReq is the request to obtain the undelegation hold count.
message UndelegationHoldCountReq {
// staker_id is the staker id.
string staker_id = 1;
// asset_id is the asset id.
string asset_id = 2;
// undelegation_id is the undelegation id
uint64 undelegation_id =3;
uint64 undelegation_id = 3;
}

// UndelegationHoldCountResponse is the response for the undelegation hold count.
Expand Down Expand Up @@ -106,9 +119,9 @@
// hold count, which is used to construct the genesis state
message UndelegationAndHoldCount {
// undelegation is the single undelegation record
UndelegationRecord undelegation =1;
UndelegationRecord undelegation = 1;
// hold_count represents the number of holds on this undelegation
uint64 hold_count =2;
uint64 hold_count = 2;
}

// UndelegationRecordList is the response to query undelegations.
Expand Down Expand Up @@ -164,9 +177,9 @@
option (cosmos.query.v1.module_query_safe) = true;
option (google.api.http).get = "/exocore/delegation/v1/delegations/{staker_id}/{asset_id}";
}
// SingleDelegationInfo queries the single delegation information for
// {chain, staker, asset, operator}.
rpc QuerySingleDelegationInfo(SingleDelegationInfoReq) returns (DelegationAmounts) {
// SingleDelegationInfo queries the single delegation information and the
// maximum undelegatable amount for {staker, asset, operator}.
rpc QuerySingleDelegationInfo(SingleDelegationInfoReq) returns (SingleDelegationInfoResponse) {

Check failure on line 182 in proto/exocore/delegation/v1/query.proto

View workflow job for this annotation

GitHub Actions / break-check

RPC "QuerySingleDelegationInfo" on service "Query" changed response type from "exocore.delegation.v1.DelegationAmounts" to "exocore.delegation.v1.SingleDelegationInfoResponse".
option (cosmos.query.v1.module_query_safe) = true;
option (google.api.http).get = "/exocore/delegation/v1/single_delegation/{staker_id}/{operator_addr}/{asset_id}";
}
Expand Down
12 changes: 1 addition & 11 deletions testutil/batch/tx_check.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package batch
import (
"math/big"

delegationkeeper "github.com/ExocoreNetwork/exocore/x/delegation/keeper"
sdktypes "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/query"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
Expand Down Expand Up @@ -56,16 +55,7 @@ func (m *Manager) QueryDelegatedAmount(clientChainLzID uint64, stakerAddr, asset
if err != nil {
return sdkmath.ZeroInt(), err
}
operatorAssetReq := &assettypes.QueryOperatorSpecifiedAssetAmountReq{
OperatorAddr: operatorAddr, // already lowercase
AssetId: assetID, // already lowercase
}
queryAssetsClient := assettypes.NewQueryClient(m.NodeClientCtx[DefaultNodeIndex])
operatorAssetInfo, err := queryAssetsClient.QueOperatorSpecifiedAssetAmount(m.ctx, operatorAssetReq)
if err != nil {
return sdkmath.ZeroInt(), err
}
return delegationkeeper.TokensFromShares(delegationInfo.UndelegatableShare, operatorAssetInfo.TotalShare, operatorAssetInfo.TotalAmount)
return delegationInfo.MaxUndelegatableAmount, nil
}

func (m *Manager) PrecompileTxOnChainCheck(batchID uint, msgType string) error {
Expand Down
9 changes: 5 additions & 4 deletions x/assets/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper

import (
"context"
"strings"

"github.com/cosmos/cosmos-sdk/store/prefix"
"github.com/cosmos/cosmos-sdk/types/query"
Expand Down Expand Up @@ -57,7 +58,7 @@ func (k Keeper) QueAllClientChainInfo(goCtx context.Context, req *assetstype.Que
// QueStakingAssetInfo query the specified client chain asset info by inputting assetID
func (k Keeper) QueStakingAssetInfo(ctx context.Context, info *assetstype.QueryStakingAssetInfo) (*assetstype.StakingAssetInfo, error) {
c := sdk.UnwrapSDKContext(ctx)
return k.GetStakingAssetInfo(c, info.AssetId)
return k.GetStakingAssetInfo(c, strings.ToLower(info.AssetId))
}

// QueAllStakingAssetsInfo query the info about all client chain assets that have been registered
Expand All @@ -73,7 +74,7 @@ func (k Keeper) QueAllStakingAssetsInfo(ctx context.Context, _ *assetstype.Query
// QueStakerAssetInfos query th state of all assets for a staker specified by stakerID
func (k Keeper) QueStakerAssetInfos(ctx context.Context, info *assetstype.QueryStakerAssetInfo) (*assetstype.QueryAssetInfoResponse, error) {
c := sdk.UnwrapSDKContext(ctx)
assetInfos, err := k.GetStakerAssetInfos(c, info.StakerId)
assetInfos, err := k.GetStakerAssetInfos(c, strings.ToLower(info.StakerId))
if err != nil {
return nil, err
}
Expand All @@ -83,7 +84,7 @@ func (k Keeper) QueStakerAssetInfos(ctx context.Context, info *assetstype.QueryS
// QueStakerSpecifiedAssetAmount query the specified asset state of a staker, using stakerID and assetID as query parameters
func (k Keeper) QueStakerSpecifiedAssetAmount(ctx context.Context, req *assetstype.QuerySpecifiedAssetAmountReq) (*assetstype.StakerAssetInfo, error) {
c := sdk.UnwrapSDKContext(ctx)
return k.GetStakerSpecifiedAssetInfo(c, req.StakerId, req.AssetId)
return k.GetStakerSpecifiedAssetInfo(c, strings.ToLower(req.StakerId), strings.ToLower(req.AssetId))
}

// QueOperatorAssetInfos query th state of all assets for an operator specified by operator address
Expand All @@ -107,5 +108,5 @@ func (k Keeper) QueOperatorSpecifiedAssetAmount(ctx context.Context, req *assets
if err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}
return k.GetOperatorSpecifiedAssetInfo(c, addr, req.AssetId)
return k.GetOperatorSpecifiedAssetInfo(c, addr, strings.ToLower(req.AssetId))
}
16 changes: 8 additions & 8 deletions x/delegation/keeper/delegation_op_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
errorsmod "cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"
assetskeeper "github.com/ExocoreNetwork/exocore/x/assets/keeper"
assetstypes "github.com/ExocoreNetwork/exocore/x/assets/types"

"github.com/ExocoreNetwork/exocore/x/assets/types"
delegationtype "github.com/ExocoreNetwork/exocore/x/delegation/types"
Expand Down Expand Up @@ -102,9 +103,9 @@ func (suite *DelegationTestSuite) prepareOptingInDogfood(assetID string) (sdkmat

func (suite *DelegationTestSuite) prepareDelegationNativeToken() *delegationtype.DelegationOrUndelegationParams {
delegationEvent := &delegationtype.DelegationOrUndelegationParams{
ClientChainID: types.ExocoreChainLzID,
ClientChainID: assetstypes.ExocoreChainLzID,
Action: types.DelegateTo,
AssetsAddress: common.HexToAddress(types.ExocoreAssetAddr).Bytes(),
AssetsAddress: common.HexToAddress(assetstypes.ExocoreAssetAddr).Bytes(),
OperatorAddress: suite.opAccAddr,
StakerAddress: suite.accAddr[:],
OpAmount: suite.delegationAmount,
Expand Down Expand Up @@ -176,9 +177,9 @@ func (suite *DelegationTestSuite) TestDelegateTo() {

// delegate exocore-native-token
delegationParams = &delegationtype.DelegationOrUndelegationParams{
ClientChainID: types.ExocoreChainLzID,
ClientChainID: assetstypes.ExocoreChainLzID,
Action: types.DelegateTo,
AssetsAddress: common.HexToAddress(types.ExocoreAssetAddr).Bytes(),
AssetsAddress: common.HexToAddress(assetstypes.ExocoreAssetAddr).Bytes(),
OperatorAddress: opAccAddr,
StakerAddress: suite.accAddr[:],
OpAmount: sdkmath.NewInt(50),
Expand All @@ -190,7 +191,7 @@ func (suite *DelegationTestSuite) TestDelegateTo() {
stakerID, assetID = types.GetStakerIDAndAssetID(delegationParams.ClientChainID, delegationParams.StakerAddress, delegationParams.AssetsAddress)
restakerState, err = suite.App.AssetsKeeper.GetStakerSpecifiedAssetInfo(suite.Ctx, stakerID, assetID)
suite.NoError(err)
balance := suite.App.BankKeeper.GetBalance(suite.Ctx, suite.accAddr, types.ExocoreAssetDenom)
balance := suite.App.BankKeeper.GetBalance(suite.Ctx, suite.accAddr, assetstypes.ExocoreAssetDenom)
suite.Equal(types.StakerAssetInfo{
TotalDepositAmount: balance.Amount.Add(delegationParams.OpAmount),
WithdrawableAmount: balance.Amount,
Expand Down Expand Up @@ -272,7 +273,6 @@ func (suite *DelegationTestSuite) TestAutoAssociate() {
TotalShare: sdkmath.LegacyNewDecFromBigInt(delegationParams.OpAmount.BigInt()),
OperatorShare: sdkmath.LegacyNewDecFromBigInt(delegationParams.OpAmount.BigInt()),
}, *operatorState)

}

func (suite *DelegationTestSuite) TestUndelegateFrom() {
Expand Down Expand Up @@ -344,7 +344,7 @@ func (suite *DelegationTestSuite) TestUndelegateFrom() {
stakerID, assetID = types.GetStakerIDAndAssetID(delegationEvent.ClientChainID, delegationEvent.StakerAddress, delegationEvent.AssetsAddress)
restakerState, err = suite.App.AssetsKeeper.GetStakerSpecifiedAssetInfo(suite.Ctx, stakerID, assetID)
suite.NoError(err)
balance := suite.App.BankKeeper.GetBalance(suite.Ctx, suite.accAddr, types.ExocoreAssetDenom)
balance := suite.App.BankKeeper.GetBalance(suite.Ctx, suite.accAddr, assetstypes.ExocoreAssetDenom)
suite.Equal(types.StakerAssetInfo{
TotalDepositAmount: balance.Amount.Add(delegationEvent.OpAmount),
WithdrawableAmount: balance.Amount,
Expand Down Expand Up @@ -496,7 +496,7 @@ func (suite *DelegationTestSuite) TestCompleteUndelegation() {
restakerState, err = suite.App.AssetsKeeper.GetStakerSpecifiedAssetInfo(suite.Ctx, stakerID, assetID)
suite.NoError(err)

balance := suite.App.BankKeeper.GetBalance(suite.Ctx, suite.accAddr, types.ExocoreAssetDenom)
balance := suite.App.BankKeeper.GetBalance(suite.Ctx, suite.accAddr, assetstypes.ExocoreAssetDenom)
suite.Equal(types.StakerAssetInfo{
TotalDepositAmount: balance.Amount,
WithdrawableAmount: balance.Amount,
Expand Down
30 changes: 16 additions & 14 deletions x/delegation/keeper/delegation_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,20 @@ func (k Keeper) IterateDelegationsForStaker(ctx sdk.Context, stakerID string, op
return k.IterateDelegations(ctx, []byte(stakerID), opFunc)
}

func (k Keeper) UndelegatableAmount(ctx sdk.Context, assetID, operator string, amounts *delegationtype.DelegationAmounts) (amount sdkmath.Int, err error) {
opAccAddr := sdk.MustAccAddressFromBech32(operator)
// get the asset state of operator
operatorAsset, err := k.assetsKeeper.GetOperatorSpecifiedAssetInfo(ctx, opAccAddr, assetID)
if err != nil {
return sdkmath.ZeroInt(), err
}
singleAmount, err := TokensFromShares(amounts.UndelegatableShare, operatorAsset.TotalShare, operatorAsset.TotalAmount)
if err != nil {
return sdkmath.ZeroInt(), err
}
return singleAmount, nil
}

// TotalDelegatedAmountForStakerAsset query the total delegation amount of the specified staker and asset.
// It needs to be calculated from the share and amount of the asset pool.
func (k Keeper) TotalDelegatedAmountForStakerAsset(ctx sdk.Context, stakerID string, assetID string) (amount sdkmath.Int, err error) {
Expand All @@ -80,13 +94,7 @@ func (k Keeper) TotalDelegatedAmountForStakerAsset(ctx sdk.Context, stakerID str
if amounts.UndelegatableShare.IsZero() {
return false, nil
}
opAccAddr := sdk.MustAccAddressFromBech32(keys.GetOperatorAddr())
// get the asset state of operator
operatorAsset, err := k.assetsKeeper.GetOperatorSpecifiedAssetInfo(ctx, opAccAddr, assetID)
if err != nil {
return true, err
}
singleAmount, err := TokensFromShares(amounts.UndelegatableShare, operatorAsset.TotalShare, operatorAsset.TotalAmount)
singleAmount, err := k.UndelegatableAmount(ctx, assetID, keys.GetOperatorAddr(), amounts)
if err != nil {
return true, err
}
Expand All @@ -102,13 +110,7 @@ func (k Keeper) TotalDelegatedAmountForStakerAsset(ctx sdk.Context, stakerID str
func (k *Keeper) AllDelegatedInfoForStakerAsset(ctx sdk.Context, stakerID string, assetID string) (map[string]sdkmath.Int, error) {
ret := make(map[string]sdkmath.Int)
opFunc := func(keys *delegationtype.SingleDelegationInfoReq, amounts *delegationtype.DelegationAmounts) (bool, error) {
opAccAddr := sdk.MustAccAddressFromBech32(keys.GetOperatorAddr())
// get the asset state of operator
operatorAsset, err := k.assetsKeeper.GetOperatorSpecifiedAssetInfo(ctx, opAccAddr, assetID)
if err != nil {
return true, err
}
singleAmount, err := TokensFromShares(amounts.UndelegatableShare, operatorAsset.TotalShare, operatorAsset.TotalAmount)
singleAmount, err := k.UndelegatableAmount(ctx, assetID, keys.GetOperatorAddr(), amounts)
if err != nil {
return true, err
}
Expand Down
Loading
Loading