From 225fb425093e8fc2fbdd4b6e751e75e795678a57 Mon Sep 17 00:00:00 2001 From: scorpioborn <97235353+scorpioborn@users.noreply.github.com> Date: Thu, 27 Jun 2024 09:58:33 +0300 Subject: [PATCH] feat: force at least single bet for reward grant --- app/upgrades/v10/consts.go | 4 +- x/bet/keeper/bet.go | 16 ++++ x/bet/keeper/bet_test.go | 14 ++++ x/reward/keeper/msg_server_reward_test.go | 96 +++++++++++++++++++++- x/reward/types/errors.go | 1 + x/reward/types/expected_keepers.go | 1 + x/reward/types/reward_signup.go | 8 ++ x/reward/types/reward_signup_affiliatee.go | 8 ++ x/reward/types/reward_signup_affiliator.go | 8 ++ x/reward/types/reward_signup_referee.go | 8 ++ x/reward/types/reward_signup_referrer.go | 8 ++ 11 files changed, 169 insertions(+), 3 deletions(-) diff --git a/app/upgrades/v10/consts.go b/app/upgrades/v10/consts.go index ebd706b2..c427491e 100644 --- a/app/upgrades/v10/consts.go +++ b/app/upgrades/v10/consts.go @@ -6,8 +6,8 @@ import ( "github.com/sge-network/sge/app/upgrades" ) -// UpgradeName defines the on-chain upgrade name for the v1.7.1 upgrade. -const UpgradeName = "v1.7.1" +// UpgradeName defines the on-chain upgrade name for the v1.7.2 upgrade. +const UpgradeName = "v1.7.2" var Upgrade = upgrades.Upgrade{ UpgradeName: UpgradeName, diff --git a/x/bet/keeper/bet.go b/x/bet/keeper/bet.go index 87b5d715..57001f66 100644 --- a/x/bet/keeper/bet.go +++ b/x/bet/keeper/bet.go @@ -51,6 +51,22 @@ func (k Keeper) GetBets(ctx sdk.Context) (list []types.Bet, err error) { return } +// IsAnyBetForAccount checks if there is any bet for the account +func (k Keeper) IsAnyBetForAccount(ctx sdk.Context, creator string) (thereIs bool, err error) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.BetListByCreatorPrefix(creator)) + + // create iterator for all existing records + iterator := sdk.KVStorePrefixIterator(store, []byte{}) + defer func() { + err = iterator.Close() + }() + + // check if the iterator has any records + thereIs = iterator.Valid() + + return +} + // SetBetID sets a specific bet id map in the store func (k Keeper) SetBetID(ctx sdk.Context, uid2ID types.UID2ID) { store := k.getBetIDStore(ctx) diff --git a/x/bet/keeper/bet_test.go b/x/bet/keeper/bet_test.go index 7f5ef1a2..0e5d2634 100644 --- a/x/bet/keeper/bet_test.go +++ b/x/bet/keeper/bet_test.go @@ -10,6 +10,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/sge-network/sge/testutil/nullify" + "github.com/sge-network/sge/testutil/sample" "github.com/sge-network/sge/testutil/simapp" "github.com/sge-network/sge/x/bet/keeper" "github.com/sge-network/sge/x/bet/types" @@ -104,3 +105,16 @@ func TestSortBetGetAll(t *testing.T) { nullify.Fill(bets), ) } + +func TestBetIsAnyBetForAccount(t *testing.T) { + tApp, k, ctx := setupKeeperAndApp(t) + items := createNBet(tApp, k, ctx, 10) + + thereIs, err := k.IsAnyBetForAccount(ctx, items[0].Creator) + require.NoError(t, err) + require.True(t, thereIs) + + thereIs, err = k.IsAnyBetForAccount(ctx, sample.AccAddress()) + require.NoError(t, err) + require.False(t, thereIs) +} diff --git a/x/reward/keeper/msg_server_reward_test.go b/x/reward/keeper/msg_server_reward_test.go index 36630b4a..6c4856b1 100644 --- a/x/reward/keeper/msg_server_reward_test.go +++ b/x/reward/keeper/msg_server_reward_test.go @@ -86,6 +86,14 @@ func TestMsgApplySignupReward(t *testing.T) { promoter := simapp.TestParamUsers["user1"].Address.String() receiverAddr := simapp.TestParamUsers["user2"].Address.String() + // dummy bet to pass at least one bet condition + tApp.BetKeeper.SetBet(ctx, *bettypes.NewBet( + receiverAddr, + &bettypes.WagerProps{UID: uuid.NewString()}, + &bettypes.BetOdds{}, + bettypes.MetaData{}, + ), 1) + _, err := tApp.SubaccountKeeper.CreateSubaccount(ctx, receiverAddr, receiverAddr, []subaccounttypes.LockedBalance{ { Amount: sdkmath.ZeroInt(), @@ -163,6 +171,14 @@ func TestMsgApplySignupRewardWithCap(t *testing.T) { promoter := simapp.TestParamUsers["user1"].Address.String() receiverAddr := simapp.TestParamUsers["user2"].Address.String() + // dummy bet to pass at least one bet condition + tApp.BetKeeper.SetBet(ctx, *bettypes.NewBet( + receiverAddr, + &bettypes.WagerProps{UID: uuid.NewString()}, + &bettypes.BetOdds{}, + bettypes.MetaData{}, + ), 1) + _, err := tApp.SubaccountKeeper.CreateSubaccount(ctx, receiverAddr, receiverAddr, []subaccounttypes.LockedBalance{ { Amount: sdkmath.ZeroInt(), @@ -221,6 +237,14 @@ func TestMsgApplySignupRefereeReward(t *testing.T) { referrer := simapp.TestParamUsers["user3"].Address.String() + // dummy bet to pass at least one bet condition + tApp.BetKeeper.SetBet(ctx, *bettypes.NewBet( + receiverAddr, + &bettypes.WagerProps{UID: uuid.NewString()}, + &bettypes.BetOdds{}, + bettypes.MetaData{}, + ), 1) + _, err := tApp.SubaccountKeeper.CreateSubaccount(ctx, receiverAddr, receiverAddr, []subaccounttypes.LockedBalance{ { Amount: sdkmath.ZeroInt(), @@ -314,6 +338,26 @@ func TestMsgApplySignupReferrerReward(t *testing.T) { referee := simapp.TestParamUsers["user3"].Address.String() referrer := simapp.TestParamUsers["user4"].Address.String() + // dummy bet to pass at least one bet condition + tApp.BetKeeper.SetBet(ctx, *bettypes.NewBet( + referrer, + &bettypes.WagerProps{UID: uuid.NewString()}, + &bettypes.BetOdds{}, + bettypes.MetaData{}, + ), 1) + tApp.BetKeeper.SetBet(ctx, *bettypes.NewBet( + referee, + &bettypes.WagerProps{UID: uuid.NewString()}, + &bettypes.BetOdds{}, + bettypes.MetaData{}, + ), 1) + tApp.BetKeeper.SetBet(ctx, *bettypes.NewBet( + receiverAddr, + &bettypes.WagerProps{UID: uuid.NewString()}, + &bettypes.BetOdds{}, + bettypes.MetaData{}, + ), 1) + _, err := tApp.SubaccountKeeper.CreateSubaccount(ctx, receiverAddr, receiverAddr, []subaccounttypes.LockedBalance{ { Amount: sdkmath.ZeroInt(), @@ -453,6 +497,14 @@ func TestMsgApplySignupAffiliateReward(t *testing.T) { leadGen := simapp.TestParamUsers["user3"].Address.String() + // dummy bet to pass at least one bet condition + tApp.BetKeeper.SetBet(ctx, *bettypes.NewBet( + receiverAddr, + &bettypes.WagerProps{UID: uuid.NewString()}, + &bettypes.BetOdds{}, + bettypes.MetaData{}, + ), 1) + _, err := tApp.SubaccountKeeper.CreateSubaccount(ctx, receiverAddr, receiverAddr, []subaccounttypes.LockedBalance{ { Amount: sdkmath.ZeroInt(), @@ -546,6 +598,26 @@ func TestMsgApplySignupAffiliateeReward(t *testing.T) { affiliatee := simapp.TestParamUsers["user3"].Address.String() affiliator := simapp.TestParamUsers["user4"].Address.String() + // dummy bet to pass at least one bet condition + tApp.BetKeeper.SetBet(ctx, *bettypes.NewBet( + affiliatee, + &bettypes.WagerProps{UID: uuid.NewString()}, + &bettypes.BetOdds{}, + bettypes.MetaData{}, + ), 1) + tApp.BetKeeper.SetBet(ctx, *bettypes.NewBet( + affiliator, + &bettypes.WagerProps{UID: uuid.NewString()}, + &bettypes.BetOdds{}, + bettypes.MetaData{}, + ), 1) + tApp.BetKeeper.SetBet(ctx, *bettypes.NewBet( + receiverAddr, + &bettypes.WagerProps{UID: uuid.NewString()}, + &bettypes.BetOdds{}, + bettypes.MetaData{}, + ), 1) + _, err := tApp.SubaccountKeeper.CreateSubaccount(ctx, receiverAddr, receiverAddr, []subaccounttypes.LockedBalance{ { Amount: sdkmath.ZeroInt(), @@ -845,6 +917,21 @@ func TestMsgApplySignupRewardSubaccount(t *testing.T) { }) require.NoError(t, err) + noSubaccountAddr := sample.AccAddress() + // dummy bet to pass at least one bet condition + tApp.BetKeeper.SetBet(ctx, *bettypes.NewBet( + receiverAddr, + &bettypes.WagerProps{UID: uuid.NewString()}, + &bettypes.BetOdds{}, + bettypes.MetaData{}, + ), 1) + tApp.BetKeeper.SetBet(ctx, *bettypes.NewBet( + noSubaccountAddr, + &bettypes.WagerProps{UID: uuid.NewString()}, + &bettypes.BetOdds{}, + bettypes.MetaData{}, + ), 1) + for _, tc := range []struct { desc string claims jwt.MapClaims @@ -883,7 +970,7 @@ func TestMsgApplySignupRewardSubaccount(t *testing.T) { "exp": time.Now().Add(time.Minute * 5).Unix(), "iat": time.Now().Unix(), "common": types.RewardPayloadCommon{ - Receiver: sample.AccAddress(), + Receiver: noSubaccountAddr, SourceUID: "source id", Meta: "signup reward for sample user", KycData: &sgetypes.KycDataPayload{ @@ -939,6 +1026,13 @@ func TestMsgApplySubaccountFunds(t *testing.T) { promoter := simapp.TestParamUsers["user1"].Address.String() receiverAddr := simapp.TestParamUsers["user2"].Address.String() + tApp.BetKeeper.SetBet(ctx, *bettypes.NewBet( + receiverAddr, + &bettypes.WagerProps{UID: uuid.NewString()}, + &bettypes.BetOdds{}, + bettypes.MetaData{}, + ), 1) + rewardAmount := int64(100) campClaims := getDefaultClaim(promoter) diff --git a/x/reward/types/errors.go b/x/reward/types/errors.go index 335a0fd5..71dc3883 100644 --- a/x/reward/types/errors.go +++ b/x/reward/types/errors.go @@ -40,4 +40,5 @@ var ( ErrDuplicateCategoryInConf = sdkerrors.Register(ModuleName, 7130, "duplicate category in promoter configurations") ErrCategoryCapShouldBePos = sdkerrors.Register(ModuleName, 7131, "category cap should be a positive number") ErrMissingConstraintForCampaign = sdkerrors.Register(ModuleName, 7132, "missing constraints in the campaign") + ErrNoBetForReceiverFound = sdkerrors.Register(ModuleName, 7133, "reward receiver should have at least one wager") ) diff --git a/x/reward/types/expected_keepers.go b/x/reward/types/expected_keepers.go index 3e4c2248..a3ed9d50 100644 --- a/x/reward/types/expected_keepers.go +++ b/x/reward/types/expected_keepers.go @@ -34,6 +34,7 @@ type BankKeeper interface { type BetKeeper interface { GetBet(ctx sdk.Context, creator string, id uint64) (val bettypes.Bet, found bool) GetBetID(ctx sdk.Context, uid string) (val bettypes.UID2ID, found bool) + IsAnyBetForAccount(ctx sdk.Context, creator string) (thereIs bool, err error) } // BetKeeper defines the expected interface needed to access market state. diff --git a/x/reward/types/reward_signup.go b/x/reward/types/reward_signup.go index fa19fa08..65f359d1 100644 --- a/x/reward/types/reward_signup.go +++ b/x/reward/types/reward_signup.go @@ -57,6 +57,14 @@ func (sur SignUpReward) Calculate(goCtx context.Context, ctx sdk.Context, keeper return RewardFactoryData{}, sdkerrors.Wrapf(sdkerrtypes.ErrInvalidAddress, "%s", err) } + hasBet, err := keepers.BetKeeper.IsAnyBetForAccount(ctx, payload.Common.Receiver) + if err != nil { + return RewardFactoryData{}, sdkerrors.Wrapf(sdkerrtypes.ErrPanic, "%s", err) + } + if !hasBet { + return RewardFactoryData{}, ErrNoBetForReceiverFound + } + return NewRewardFactoryData( NewReceiver( payload.Common.Receiver, diff --git a/x/reward/types/reward_signup_affiliatee.go b/x/reward/types/reward_signup_affiliatee.go index f2afa6cf..a78a34f0 100644 --- a/x/reward/types/reward_signup_affiliatee.go +++ b/x/reward/types/reward_signup_affiliatee.go @@ -61,6 +61,14 @@ func (sur SignUpAffiliateeReward) Calculate(goCtx context.Context, ctx sdk.Conte return RewardFactoryData{}, sdkerrors.Wrapf(sdkerrtypes.ErrInvalidAddress, "%s", err) } + hasBet, err := keepers.BetKeeper.IsAnyBetForAccount(ctx, payload.Common.Receiver) + if err != nil { + return RewardFactoryData{}, sdkerrors.Wrapf(sdkerrtypes.ErrPanic, "%s", err) + } + if !hasBet { + return RewardFactoryData{}, ErrNoBetForReceiverFound + } + return NewRewardFactoryData( NewReceiver( payload.Common.Receiver, diff --git a/x/reward/types/reward_signup_affiliator.go b/x/reward/types/reward_signup_affiliator.go index 75a9f542..43d31c7a 100644 --- a/x/reward/types/reward_signup_affiliator.go +++ b/x/reward/types/reward_signup_affiliator.go @@ -69,6 +69,14 @@ func (sur SignUpAffiliatorReward) Calculate(goCtx context.Context, ctx sdk.Conte return RewardFactoryData{}, sdkerrors.Wrapf(sdkerrtypes.ErrInvalidAddress, "%s", err) } + hasBet, err := keepers.BetKeeper.IsAnyBetForAccount(ctx, payload.Common.Receiver) + if err != nil { + return RewardFactoryData{}, sdkerrors.Wrapf(sdkerrtypes.ErrPanic, "%s", err) + } + if !hasBet { + return RewardFactoryData{}, ErrNoBetForReceiverFound + } + return NewRewardFactoryData( NewReceiver( payload.Common.Receiver, diff --git a/x/reward/types/reward_signup_referee.go b/x/reward/types/reward_signup_referee.go index ecde0865..c1a5f484 100644 --- a/x/reward/types/reward_signup_referee.go +++ b/x/reward/types/reward_signup_referee.go @@ -61,6 +61,14 @@ func (sur SignUpRefereelReward) Calculate(goCtx context.Context, ctx sdk.Context return RewardFactoryData{}, sdkerrors.Wrapf(sdkerrtypes.ErrInvalidAddress, "%s", err) } + hasBet, err := keepers.BetKeeper.IsAnyBetForAccount(ctx, payload.Common.Receiver) + if err != nil { + return RewardFactoryData{}, sdkerrors.Wrapf(sdkerrtypes.ErrPanic, "%s", err) + } + if !hasBet { + return RewardFactoryData{}, ErrNoBetForReceiverFound + } + return NewRewardFactoryData( NewReceiver( payload.Common.Receiver, diff --git a/x/reward/types/reward_signup_referrer.go b/x/reward/types/reward_signup_referrer.go index b1481a21..6271e64f 100644 --- a/x/reward/types/reward_signup_referrer.go +++ b/x/reward/types/reward_signup_referrer.go @@ -66,6 +66,14 @@ func (sur SignUpReferrerReward) Calculate(goCtx context.Context, ctx sdk.Context return RewardFactoryData{}, sdkerrors.Wrapf(sdkerrtypes.ErrInvalidAddress, "%s", err) } + hasBet, err := keepers.BetKeeper.IsAnyBetForAccount(ctx, payload.Common.Receiver) + if err != nil { + return RewardFactoryData{}, sdkerrors.Wrapf(sdkerrtypes.ErrPanic, "%s", err) + } + if !hasBet { + return RewardFactoryData{}, ErrNoBetForReceiverFound + } + return NewRewardFactoryData( NewReceiver( payload.Common.Receiver,