Skip to content

Commit

Permalink
feat(delegation): auto associate when possible
Browse files Browse the repository at this point in the history
Whenever the asset bearing `ExocoreAssetID` is delegated by a staker to
an operator, we should check if the staker address is the same as the
operator address. If a match is found, they should be automatically
associated.
  • Loading branch information
MaxMustermann2 committed Jan 12, 2025
1 parent 59241ba commit 4794bf3
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 15 deletions.
12 changes: 6 additions & 6 deletions testutil/fund.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@ package testutil

import (
"cosmossdk.io/math"
"github.com/ExocoreNetwork/exocore/utils"
exominttypes "github.com/ExocoreNetwork/exocore/x/exomint/types"
sdk "github.com/cosmos/cosmos-sdk/types"
bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
"github.com/evmos/evmos/v16/utils"
inflationtypes "github.com/evmos/evmos/v16/x/inflation/v1/types"
)

// FundAccount is a utility function that funds an account by minting and
// sending the coins to the address.
func FundAccount(ctx sdk.Context, bankKeeper bankkeeper.Keeper, addr sdk.AccAddress, amounts sdk.Coins) error {
if err := bankKeeper.MintCoins(ctx, inflationtypes.ModuleName, amounts); err != nil {
if err := bankKeeper.MintCoins(ctx, exominttypes.ModuleName, amounts); err != nil {
return err
}

return bankKeeper.SendCoinsFromModuleToAccount(ctx, inflationtypes.ModuleName, addr, amounts)
return bankKeeper.SendCoinsFromModuleToAccount(ctx, exominttypes.ModuleName, addr, amounts)
}

// FundAccountWithBaseDenom is a utility function that uses the FundAccount function
Expand All @@ -30,9 +30,9 @@ func FundAccountWithBaseDenom(ctx sdk.Context, bankKeeper bankkeeper.Keeper, add
// FundModuleAccount is a utility function that funds a module account by
// minting and sending the coins to the address.
func FundModuleAccount(ctx sdk.Context, bankKeeper bankkeeper.Keeper, recipientMod string, amounts sdk.Coins) error {
if err := bankKeeper.MintCoins(ctx, inflationtypes.ModuleName, amounts); err != nil {
if err := bankKeeper.MintCoins(ctx, exominttypes.ModuleName, amounts); err != nil {
return err
}

return bankKeeper.SendCoinsFromModuleToModule(ctx, inflationtypes.ModuleName, recipientMod, amounts)
return bankKeeper.SendCoinsFromModuleToModule(ctx, exominttypes.ModuleName, recipientMod, amounts)
}
13 changes: 13 additions & 0 deletions x/delegation/keeper/delegation.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package keeper

import (
"bytes"
"fmt"

errorsmod "cosmossdk.io/errors"
Expand Down Expand Up @@ -62,6 +63,13 @@ func (k *Keeper) delegateTo(
if err := k.bankKeeper.DelegateCoinsFromAccountToModule(ctx, params.StakerAddress, delegationtype.DelegatedPoolName, coins); err != nil {
return err
}
// auto associate it, if there is a match. note that both are byte versions of bech32 AccAddress.
if bytes.Equal(params.StakerAddress, params.OperatorAddress[:]) {
err := k.SetAssociatedOperator(ctx, stakerID, params.OperatorAddress.String())
if err != nil {
return err
}
}
}
// calculate the share from the delegation amount
share, err := k.CalculateShare(ctx, params.OperatorAddress, assetID, params.OpAmount)
Expand Down Expand Up @@ -149,6 +157,11 @@ func (k *Keeper) UndelegateFrom(ctx sdk.Context, params *delegationtype.Delegati
}
r.CompletedEpochIdentifier = completedEpochID
r.CompletedEpochNumber = completedEpochNumber
// the hold count is relevant to async AVSs instead of sync AVSs. for example, the dogfood AVS is sync since it
// runs only on this chain. meanwhile, x/appchain-based AVSs are async because of the IBC's in-built communication
// lag. the hold count is used to ensure that the undelegation is not processed until the AVS has completed its
// unbonding period.
// TODO: remove the hold count increment for x/dogfood AVS.
err = k.SetUndelegationRecords(ctx, false, []delegationtype.UndelegationAndHoldCount{
{
Undelegation: &r,
Expand Down
58 changes: 49 additions & 9 deletions x/delegation/keeper/delegation_op_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@ package keeper_test

import (
"fmt"
epochtypes "github.com/ExocoreNetwork/exocore/x/epochs/types"
"math"
"time"

"github.com/ExocoreNetwork/exocore/testutil"
epochtypes "github.com/ExocoreNetwork/exocore/x/epochs/types"

utiltx "github.com/ExocoreNetwork/exocore/testutil/tx"
avstypes "github.com/ExocoreNetwork/exocore/x/avs/types"

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 @@ -100,9 +102,9 @@ func (suite *DelegationTestSuite) prepareOptingInDogfood(assetID string) (sdkmat

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

// delegate exocore-native-token
delegationParams = &delegationtype.DelegationOrUndelegationParams{
ClientChainID: assetstypes.ExocoreChainLzID,
ClientChainID: types.ExocoreChainLzID,
Action: types.DelegateTo,
AssetsAddress: common.HexToAddress(assetstypes.ExocoreAssetAddr).Bytes(),
AssetsAddress: common.HexToAddress(types.ExocoreAssetAddr).Bytes(),
OperatorAddress: opAccAddr,
StakerAddress: suite.accAddr[:],
OpAmount: sdkmath.NewInt(50),
Expand All @@ -188,7 +190,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, assetstypes.ExocoreAssetDenom)
balance := suite.App.BankKeeper.GetBalance(suite.Ctx, suite.accAddr, types.ExocoreAssetDenom)
suite.Equal(types.StakerAssetInfo{
TotalDepositAmount: balance.Amount.Add(delegationParams.OpAmount),
WithdrawableAmount: balance.Amount,
Expand All @@ -215,6 +217,44 @@ func (suite *DelegationTestSuite) TestDelegateTo() {
suite.Equal(delegationParams.OpAmount, totalDelegationAmount)
}

func (suite *DelegationTestSuite) TestAutoAssociate() {
genAddr := utiltx.GenerateAddress()
opAccAddr := sdk.AccAddress(genAddr[:])

registerReq := &operatortype.RegisterOperatorReq{
FromAddress: opAccAddr.String(),
Info: &operatortype.OperatorInfo{
EarningsAddr: opAccAddr.String(),
},
}
_, err := s.OperatorMsgServer.RegisterOperator(s.Ctx, registerReq)
suite.NoError(err)

// self delegate exocore-native-token
err = testutil.FundAccountWithBaseDenom(
suite.Ctx, suite.App.BankKeeper, opAccAddr, math.MaxInt64,
)
suite.NoError(err)
delegationParams := &delegationtype.DelegationOrUndelegationParams{
ClientChainID: types.ExocoreChainLzID,
Action: types.DelegateTo,
AssetsAddress: common.HexToAddress(types.ExocoreAssetAddr).Bytes(),
OperatorAddress: opAccAddr,
StakerAddress: opAccAddr,
OpAmount: sdkmath.NewInt(50),
TxHash: common.HexToHash("0x24c4a315d757249c12a7a1d7b6fb96261d49deee26f06a3e1787d008b445c3ac"),
}
err = suite.App.DelegationKeeper.DelegateTo(suite.Ctx, delegationParams)
suite.NoError(err)
operatorID, _ := types.GetStakerIDAndAssetID(
delegationParams.ClientChainID, delegationParams.StakerAddress, delegationParams.AssetsAddress,
)
operator, err := suite.App.DelegationKeeper.GetAssociatedOperator(suite.Ctx, operatorID)
suite.NoError(err)
suite.Equal(opAccAddr.String(), operator)

}

func (suite *DelegationTestSuite) TestUndelegateFrom() {
suite.basicPrepare()
suite.prepareDeposit(suite.depositAmount)
Expand Down Expand Up @@ -284,7 +324,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, assetstypes.ExocoreAssetDenom)
balance := suite.App.BankKeeper.GetBalance(suite.Ctx, suite.accAddr, types.ExocoreAssetDenom)
suite.Equal(types.StakerAssetInfo{
TotalDepositAmount: balance.Amount.Add(delegationEvent.OpAmount),
WithdrawableAmount: balance.Amount,
Expand Down Expand Up @@ -436,7 +476,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, assetstypes.ExocoreAssetDenom)
balance := suite.App.BankKeeper.GetBalance(suite.Ctx, suite.accAddr, types.ExocoreAssetDenom)
suite.Equal(types.StakerAssetInfo{
TotalDepositAmount: balance.Amount,
WithdrawableAmount: balance.Amount,
Expand Down

0 comments on commit 4794bf3

Please sign in to comment.