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

Feature / Reward grant validations #290

Merged
5 changes: 4 additions & 1 deletion proto/sge/reward/campaign.proto
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,12 @@ message Campaign {
// is_active is the flag to check if the campaign is active or not.
bool is_active = 11;

// claims_per_category is the number of times a user can claim a reward for category of this campaign.
uint64 claims_per_category = 12;

// meta is the metadata of the campaign.
// It is a stringified base64 encoded json.
string meta = 12;
string meta = 13;
}

// Pool is the type for the campaign funding pool.
Expand Down
36 changes: 31 additions & 5 deletions proto/sge/reward/reward.proto
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ message Reward {
string meta = 12;
}

// RewardAmount
message RewardAmount {
// main account reward amount
string main_account_amount = 1 [
Expand All @@ -66,11 +67,36 @@ message RewardAmount {
(gogoproto.moretags) = "yaml:\"subaccount_amount\""
];

// unlock timestamp
uint64 unlock_ts = 3 [
(gogoproto.customname) = "UnlockTS",
(gogoproto.jsontag) = "unlock_ts",
json_name = "unlock_ts"
// unlock period
uint64 unlock_period = 3 [
(gogoproto.customname) = "UnlockPeriod",
(gogoproto.jsontag) = "unlock_period",
json_name = "unlock_period"
];
}

// RewardByCategory
message RewardByCategory {
string uid = 1 [
(gogoproto.customname) = "UID",
(gogoproto.jsontag) = "uid",
json_name = "uid"
];
string addr = 2;
RewardCategory reward_category = 3;
}

// RewardByCampaign
message RewardByCampaign {
string uid = 1 [
(gogoproto.customname) = "UID",
(gogoproto.jsontag) = "uid",
json_name = "uid"
];
string campaign_uid = 2 [
(gogoproto.customname) = "CampaignUID",
(gogoproto.jsontag) = "campaign_uid",
json_name = "campaign_uid"
];
}

Expand Down
17 changes: 5 additions & 12 deletions proto/sge/reward/ticket.proto
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package sgenetwork.sge.reward;

import "gogoproto/gogo.proto";
import "sge/reward/reward.proto";
import "sge/reward/campaign.proto";

option go_package = "github.com/sge-network/sge/x/reward/types";

Expand Down Expand Up @@ -41,9 +40,12 @@ message CreateCampaignPayload {
// is_active is the flag to check if the campaign is active or not.
bool is_active = 9;

// claims_per_category is the number of times a user can claim a reward for category of this campaign.
uint64 claims_per_category = 10;

// meta is the metadata of the campaign.
// It is a stringified base64 encoded json.
string meta = 10;
string meta = 11;
}

// UpdateCampaignPayload is the type for campaign update payload.
Expand All @@ -64,18 +66,9 @@ message WithdrawFundsPayload {
// promoter is the address of campaign promoter.
// Funds would be transferred to this account.
string promoter = 1;

// amount is the funds that needs to be withdrawn.
string amount = 8 [
(gogoproto.customtype) = "cosmossdk.io/math.Int",
(gogoproto.nullable) = false,
(gogoproto.moretags) = "yaml:\"amount\""
];

// is_active is the flag to check if the campaign is active or not.
bool is_active = 9;
}

// RewardPayloadCommon
message RewardPayloadCommon {
// receiver is the address of the account that receives the reward.
string receiver = 1;
Expand Down
2 changes: 0 additions & 2 deletions proto/sge/reward/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ syntax = "proto3";

package sgenetwork.sge.reward;

import "gogoproto/gogo.proto";

option go_package = "github.com/sge-network/sge/x/reward/types";

// Msg defines the Msg service.
Expand Down
1 change: 0 additions & 1 deletion proto/sge/subaccount/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ message MsgTopUpResponse {}

// MsgWithdrawUnlockedBalances defines the Msg/WithdrawUnlockedBalances request
// type.

message MsgWithdrawUnlockedBalances {
// creator is the subaccount owner.
string creator = 1;
Expand Down
4 changes: 2 additions & 2 deletions x/reward/client/cli/query_reward.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,15 +122,15 @@ func CmdGetRewardsByCampaign() *cobra.Command {

queryClient := types.NewQueryClient(clientCtx)

argCampaignId := args[0]
argCampaignID := args[0]

pageReq, err := client.ReadPageRequest(cmd.Flags())
if err != nil {
return err
}

params := &types.QueryRewardsByCampaignRequest{
CampaignUid: argCampaignId,
CampaignUid: argCampaignID,
Pagination: pageReq,
}

Expand Down
10 changes: 6 additions & 4 deletions x/reward/client/cli/tx_reward.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,16 @@ import (

func CmdGrantReward() *cobra.Command {
cmd := &cobra.Command{
Use: "apply [campaign uid] [ticket]",
Use: "apply [uid] [campaign uid] [ticket]",
Short: "Apply a new reward",
Args: cobra.ExactArgs(2),
Args: cobra.ExactArgs(3),
RunE: func(cmd *cobra.Command, args []string) (err error) {
// Get indexes
argCampaignUID := args[0]
argUID := args[0]
argCampaignUID := args[1]

// Get value arguments
argTicket := args[1]
argTicket := args[2]

clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
Expand All @@ -29,6 +30,7 @@ func CmdGrantReward() *cobra.Command {

msg := types.NewMsgGrantReward(
clientCtx.GetFromAddress().String(),
argUID,
argCampaignUID,
argTicket,
)
Expand Down
2 changes: 1 addition & 1 deletion x/reward/keeper/campaign.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func (k Keeper) GetAllCampaign(ctx sdk.Context) (list []types.Campaign) {
}

func (k Keeper) UpdateCampaignPool(ctx sdk.Context, campaign types.Campaign, receiver types.Receiver) {
totalAmount := receiver.Amount.Add(receiver.Amount)
totalAmount := receiver.SubAccountAmount.Add(receiver.MainAccountAmount)
campaign.Pool.Spent = campaign.Pool.Spent.Add(totalAmount)

k.SetCampaign(ctx, campaign)
Expand Down
31 changes: 15 additions & 16 deletions x/reward/keeper/distribution.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,33 @@ package keeper

import (
sdkerrors "cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/sge-network/sge/x/reward/types"
subaccounttypes "github.com/sge-network/sge/x/subaccount/types"
)

// DistributeRewards distributes the rewards according to the input distribution list.
func (k Keeper) DistributeRewards(ctx sdk.Context, funderAddr string, isSubAccount bool, receiver types.Receiver) error {
if isSubAccount {
if _, err := k.subaccountKeeper.TopUp(ctx, funderAddr, receiver.Addr,
func (k Keeper) DistributeRewards(ctx sdk.Context, receiver types.Receiver) error {
if receiver.SubAccountAmount.GT(sdk.ZeroInt()) {
moduleAccAddr := types.RewardPoolFunder{}.GetModuleAcc()
if _, err := k.subaccountKeeper.TopUp(ctx, k.accountKeeper.GetModuleAddress(moduleAccAddr).String(), receiver.MainAccountAddr,
[]subaccounttypes.LockedBalance{
{
UnlockTS: receiver.UnlockTS,
Amount: receiver.Amount,
UnlockTS: uint64(ctx.BlockTime().Unix()) + receiver.UnlockPeriod,
Amount: receiver.SubAccountAmount,
},
}); err != nil {
return sdkerrors.Wrapf(types.ErrSubAccRewardTopUp, "subaccount address %s, %s", receiver.Addr, err)
return sdkerrors.Wrapf(types.ErrSubAccRewardTopUp, "subaccount address %s, %s", receiver.SubAccountAddr, err)
}
} else {
if receiver.Amount.GT(sdkmath.ZeroInt()) {
if err := k.modFunder.Refund(
types.RewardPoolFunder{}, ctx,
sdk.MustAccAddressFromBech32(receiver.Addr),
receiver.Amount,
); err != nil {
return err
}
}
if receiver.MainAccountAmount.GT(sdk.ZeroInt()) {
if err := k.modFunder.Refund(
types.RewardPoolFunder{}, ctx,
sdk.MustAccAddressFromBech32(receiver.MainAccountAddr),
receiver.MainAccountAmount,
); err != nil {
return err
}
}

Expand Down
2 changes: 2 additions & 0 deletions x/reward/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type (
memKey storetypes.StoreKey
paramstore paramtypes.Subspace
modFunder *utils.ModuleAccFunder
accountKeeper types.AccountKeeper
authzKeeper types.AuthzKeeper
betKeeper types.BetKeeper
ovmKeeper types.OVMKeeper
Expand Down Expand Up @@ -60,6 +61,7 @@ func NewKeeper(
expectedKeepers.AccountKeeper,
types.ErrorBank,
),
accountKeeper: expectedKeepers.AccountKeeper,
betKeeper: betKeeper,
ovmKeeper: ovmKeeper,
subaccountKeeper: subaccountKeeper,
Expand Down
61 changes: 58 additions & 3 deletions x/reward/keeper/msg_server_campaign.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package keeper
import (
"context"

sdkmath "cosmossdk.io/math"

sdkerrors "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrtypes "github.com/cosmos/cosmos-sdk/types/errors"
Expand Down Expand Up @@ -38,7 +40,7 @@ func (k msgServer) CreateCampaign(goCtx context.Context, msg *types.MsgCreateCam

campaign := types.NewCampaign(
msg.Creator, payload.Promoter, msg.Uid,
payload.StartTs, payload.EndTs,
payload.StartTs, payload.EndTs, payload.ClaimsPerCategory,
payload.RewardType,
payload.Category,
payload.RewardAmountType,
Expand All @@ -53,7 +55,7 @@ func (k msgServer) CreateCampaign(goCtx context.Context, msg *types.MsgCreateCam
return nil, err
}

err = rewardFactory.VaidateCampaign(campaign)
err = rewardFactory.VaidateCampaign(campaign, uint64(ctx.BlockTime().Unix()))
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -92,7 +94,7 @@ func (k msgServer) UpdateCampaign(goCtx context.Context, msg *types.MsgUpdateCam
return nil, sdkerrors.Wrap(sdkerrtypes.ErrKeyNotFound, "index not set")
}

// Checks if the the msg creator is the same as the current owner
// Checks if the msg creator is the same as the current owner
if msg.Creator != valFound.Promoter {
if err := utils.ValidateMsgAuthorization(k.authzKeeper, ctx, msg.Creator, valFound.Promoter, msg,
types.ErrAuthorizationNotFound, types.ErrAuthorizationNotAccepted); err != nil {
Expand All @@ -108,3 +110,56 @@ func (k msgServer) UpdateCampaign(goCtx context.Context, msg *types.MsgUpdateCam

return &types.MsgUpdateCampaignResponse{}, nil
}

func (k msgServer) WithdrawFunds(goCtx context.Context, msg *types.MsgWithdrawFunds) (*types.MsgWithdrawFundsResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)

var payload types.WithdrawFundsPayload
if err := k.ovmKeeper.VerifyTicketUnmarshal(goCtx, msg.Ticket, &payload); err != nil {
return nil, sdkerrors.Wrapf(types.ErrInTicketVerification, "%s", err)
}

// Validate ticket payload
if err := payload.Validate(); err != nil {
return nil, err
}

// Check if the campaign exists
valFound, isFound := k.GetCampaign(ctx, msg.Uid)
if !isFound {
return nil, sdkerrors.Wrap(sdkerrtypes.ErrKeyNotFound, "campaign not found")
}

// Checks if the msg creator is the same as the current owner
if msg.Creator != valFound.Promoter {
if err := utils.ValidateMsgAuthorization(k.authzKeeper, ctx, msg.Creator, valFound.Promoter, msg,
types.ErrAuthorizationNotFound, types.ErrAuthorizationNotAccepted); err != nil {
return nil, err
}
}
availableAmount := valFound.Pool.Total.Sub(valFound.Pool.Spent)
// check if the pool amount is positive
if availableAmount.IsNil() || !availableAmount.GT(sdkmath.ZeroInt()) {
return nil, sdkerrors.Wrapf(types.ErrWithdrawFromCampaignPool, "pool amount should be positive")
}

// transfer the funds present in campaign to the promoter
if err := k.modFunder.Refund(
types.RewardPoolFunder{}, ctx,
sdk.MustAccAddressFromBech32(payload.Promoter),
availableAmount,
); err != nil {
return nil, sdkerrors.Wrapf(types.ErrWithdrawFromCampaignPool, "%s", err)
}
// set the pool amount to zero
valFound.Pool.Total = sdkmath.ZeroInt()
// deactivate the campaign
valFound.IsActive = false

// store the campaign
k.SetCampaign(ctx, valFound)
// emit withdraw event
msg.EmitEvent(&ctx, msg.Uid)

return &types.MsgWithdrawFundsResponse{}, nil
}
4 changes: 2 additions & 2 deletions x/reward/keeper/msg_server_campaign_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func TestCampaignMsgServerCreate(t *testing.T) {
"reward_amount_type": types.RewardAmountType_REWARD_AMOUNT_TYPE_FIXED,
"reward_amount": types.RewardAmount{
SubaccountAmount: sdkmath.NewInt(100),
UnlockTS: uint64(ctx.BlockTime().Add(10 * time.Minute).Unix()),
UnlockPeriod: uint64(ctx.BlockTime().Add(10 * time.Minute).Unix()),
},
"total_funds": sdkmath.NewInt(1000000),
"is_active": true,
Expand Down Expand Up @@ -113,7 +113,7 @@ func TestCampaignMsgServerUpdate(t *testing.T) {
"reward_amount_type": types.RewardAmountType_REWARD_AMOUNT_TYPE_FIXED,
"reward_amount": types.RewardAmount{
SubaccountAmount: sdkmath.NewInt(100),
UnlockTS: uint64(ctx.BlockTime().Add(10 * time.Minute).Unix()),
UnlockPeriod: uint64(ctx.BlockTime().Add(10 * time.Minute).Unix()),
},
"total_funds": sdkmath.NewInt(1000000),
"is_active": true,
Expand Down
Loading
Loading