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(dividends): module scaffolding #686

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
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
752 changes: 752 additions & 0 deletions github.com/dymensionxyz/dymension-rdk/x/dividends/types/events.pb.go

Large diffs are not rendered by default.

1,777 changes: 1,777 additions & 0 deletions github.com/dymensionxyz/dymension-rdk/x/dividends/types/gauge.pb.go

Large diffs are not rendered by default.

Large diffs are not rendered by default.

25 changes: 25 additions & 0 deletions proto/dividends/events.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
syntax = "proto3";
package rollapp.dividends;

import "gogoproto/gogo.proto";
import "cosmos_proto/cosmos.proto";
import "dividends/gauge.proto";

option go_package = "github.com/dymensionxyz/dymension-rdk/x/dividends/types";

message EventUpdateParams {
string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
Params new_params = 2 [ (gogoproto.nullable) = false ];
Params old_params = 3 [ (gogoproto.nullable) = false ];
}

message EventCreateGauge {
// Authority is the address that controls the module.
string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
// query_condition is *where* the gauge rewards are distributed to
QueryCondition query_condition = 2 [ (gogoproto.nullable) = false ];
// vesting_condition is *how long* the gauge rewards are distributed to
VestingCondition vesting_condition = 3 [ (gogoproto.nullable) = false ];
// vesting_condition is *how frequent* the gauge rewards are distributed to
VestingFrequency vesting_frequency = 4;
}
75 changes: 75 additions & 0 deletions proto/dividends/gauge.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
syntax = "proto3";
package rollapp.dividends;

import "gogoproto/gogo.proto";

option go_package = "github.com/dymensionxyz/dymension-rdk/x/dividends/types";

// Params holds parameters for the incentives module
message Params {
// distr_epoch_identifier is what epoch type distribution will be triggered by
// (day, week, etc.)
string distr_epoch_identifier = 1;
// approved_denoms is a list of allowed tokens: only gov can approve tokens
// that can be used for dividends
repeated string approved_denoms = 2;
}

// Gauge is an object that stores and distributes yields to recipients who
// satisfy certain conditions.
message Gauge {
// id is the unique ID of a gauge
uint64 id = 1;
// address is a bech32-formatted address that holds the tokens to allocate
string address = 2;
// query_condition is *where* the gauge rewards are distributed to
QueryCondition query_condition = 3 [ (gogoproto.nullable) = false ];
// vesting_condition is *how long* the gauge rewards are distributed to
VestingCondition vesting_condition = 4 [ (gogoproto.nullable) = false ];
// vesting_condition is *how frequent* the gauge rewards are distributed to
VestingFrequency vesting_frequency = 5;
}

message QueryCondition {
oneof condition {
QueryConditionStakers stakers = 1;
}
}

message VestingCondition {
oneof condition {
VestingConditionPerpetual perpetual = 1;
VestingConditionLimited limited = 2;
}
}

enum VestingFrequency {
VESTING_FREQUENCY_UNSPECIFIED = 0;
VESTING_FREQUENCY_BLOCK = 1;
VESTING_FREQUENCY_EPOCH = 2;
}

// QueryConditionStakers queries the stakers
message QueryConditionStakers {}

// VestingConditionPerpetual is a vesting condition that distributes rewards
// infinitely. Perpetual gauges distribute all their tokens at a single time
// and only distribute their tokens again once the gauge is refilled.
//
// Non-perpetual gauges distribute their tokens equally per period while the
// gauge is in the active period. Perpetual gauges distribute all their tokens
// at a single time and only distribute their tokens again once the gauge is
// refilled.
message VestingConditionPerpetual {}

// VestingConditionLimited is a vesting condition that distributes rewards over
// the specified time. Non-perpetual gauges distribute their tokens equally per
// period while the gauge is in the active period.
message VestingConditionLimited {
// num_units is the number of total epochs/blocks distribution will be
// completed over
int64 num_units = 1;
// filled_epochs is the number of epochs/blocks distribution has been
// completed on already
int64 filled_units = 2;
}
19 changes: 19 additions & 0 deletions proto/dividends/genesis.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
syntax = "proto3";
package rollapp.dividends;

import "gogoproto/gogo.proto";
import "dividends/gauge.proto";

option go_package = "github.com/dymensionxyz/dymension-rdk/x/dividends/types";

// GenesisState defines the incentives module's various parameters when first
// initialized
message GenesisState {
// params are all the parameters of the module
Params params = 1 [ (gogoproto.nullable) = false ];
// gauges are all gauges that should exist at genesis
repeated Gauge gauges = 2 [ (gogoproto.nullable) = false ];
// last_gauge_id is what the gauge number will increment from when creating
// the next gauge after genesis
uint64 last_gauge_id = 3;
}
47 changes: 47 additions & 0 deletions proto/dividends/query.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
syntax = "proto3";
package rollapp.dividends;

import "gogoproto/gogo.proto";
import "google/api/annotations.proto";
import "cosmos/base/query/v1beta1/pagination.proto";
import "dividends/gauge.proto";

option go_package = "github.com/dymensionxyz/dymension-rdk/x/dividends/types";

// Query defines the gRPC querier service
service Query {
rpc GaugeByID(GaugeByIDRequest) returns (GaugeByIDResponse) {
option (google.api.http).get = "/rollapp/dividends/gauge_by_id/{id}";
}

rpc Gauges(GaugesRequest) returns (GaugesResponse) {
option (google.api.http).get = "/rollapp/dividends/gauges";
}

rpc Params(ParamsRequest) returns (ParamsResponse) {
option (google.api.http).get = "/rollapp/dividends/params";
}
}

message GaugeByIDRequest {
uint64 id = 1;
}

message GaugeByIDResponse {
Gauge gauge = 1 [ (gogoproto.nullable) = false ];
}

message GaugesRequest {
cosmos.base.query.v1beta1.PageRequest pagination = 1;
}

message GaugesResponse {
repeated Gauge data = 1 [ (gogoproto.nullable) = false ];
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

message ParamsRequest {}

message ParamsResponse {
Params params = 1 [ (gogoproto.nullable) = false ];
}
43 changes: 43 additions & 0 deletions proto/dividends/tx.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
syntax = "proto3";
package rollapp.dividends;

import "gogoproto/gogo.proto";
import "cosmos_proto/cosmos.proto";
import "cosmos/msg/v1/msg.proto";
import "cosmos/base/v1beta1/coin.proto";
import "dividends/gauge.proto";

option go_package = "github.com/dymensionxyz/dymension-rdk/x/dividends/types";

service Msg {
rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse);
rpc CreateGauge(MsgCreateGauge) returns (MsgCreateGaugeResponse);
}

// MsgCreateGauge creates a gague to distribute rewards to users
message MsgCreateGauge {
option (cosmos.msg.v1.signer) = "authority";

// Authority is the address that controls the module.
string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
// query_condition is *where* the gauge rewards are distributed to
QueryCondition query_condition = 2 [ (gogoproto.nullable) = false ];
// vesting_condition is *how long* the gauge rewards are distributed to
VestingCondition vesting_condition = 3 [ (gogoproto.nullable) = false ];
// vesting_condition is *how frequent* the gauge rewards are distributed to
VestingFrequency vesting_frequency = 4;
}

message MsgCreateGaugeResponse {}

// MsgUpdateParams allows to update module params.
message MsgUpdateParams {
option (cosmos.msg.v1.signer) = "authority";

// Authority is the address that controls the module.
string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
// NewParams should be fully populated.
Params new_params = 2 [ (gogoproto.nullable) = false ];
}

message MsgUpdateParamsResponse {}
1 change: 1 addition & 0 deletions swagger-proto/third_party/cosmos_tmp
Submodule cosmos_tmp added at c7b003
26 changes: 24 additions & 2 deletions testutil/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ import (
epochskeeper "github.com/dymensionxyz/dymension-rdk/x/epochs/keeper"
epochstypes "github.com/dymensionxyz/dymension-rdk/x/epochs/types"

"github.com/dymensionxyz/dymension-rdk/x/dividends"
dividendskeeper "github.com/dymensionxyz/dymension-rdk/x/dividends/keeper"
dividendstypes "github.com/dymensionxyz/dymension-rdk/x/dividends/types"

"github.com/dymensionxyz/dymension-rdk/x/gasless"
gaslessclient "github.com/dymensionxyz/dymension-rdk/x/gasless/client"
gaslesskeeper "github.com/dymensionxyz/dymension-rdk/x/gasless/keeper"
Expand Down Expand Up @@ -151,6 +155,7 @@ var kvstorekeys = []string{
epochstypes.StoreKey, hubgentypes.StoreKey, hubtypes.StoreKey,
ibctransfertypes.StoreKey, capabilitytypes.StoreKey, gaslesstypes.StoreKey, wasmtypes.StoreKey,
tokenfactorytypes.StoreKey, rollappparamstypes.StoreKey, timeupgradetypes.StoreKey,
dividendstypes.StoreKey,
}

func getGovProposalHandlers() []govclient.ProposalHandler {
Expand Down Expand Up @@ -198,6 +203,7 @@ var (
wasm.AppModuleBasic{},
tokenfactory.NewAppModuleBasic(),
rollappparams.AppModuleBasic{},
dividends.AppModuleBasic{},
)

// module account permissions
Expand All @@ -214,6 +220,7 @@ var (
wasmtypes.ModuleName: {authtypes.Burner},
rollappparamstypes.ModuleName: nil,
tokenfactorytypes.ModuleName: {authtypes.Minter, authtypes.Burner},
dividendstypes.ModuleName: nil,
}
)

Expand Down Expand Up @@ -249,6 +256,7 @@ type App struct {
HubKeeper hubkeeper.Keeper
HubGenesisKeeper hubgenkeeper.Keeper
RollappParamsKeeper rollappparamskeeper.Keeper
DividendsKeeper dividendskeeper.Keeper
UpgradeKeeper upgradekeeper.Keeper
ParamsKeeper paramskeeper.Keeper
IBCKeeper *ibckeeper.Keeper // IBC Keeper must be a pointer in the app, so we can SetRouter on it correctly
Expand Down Expand Up @@ -382,7 +390,7 @@ func NewRollapp(
)
app.MintKeeper.SetHooks(
minttypes.NewMultiMintHooks(
// insert mint hooks receivers here
// insert mint hooks receivers here
),
)

Expand All @@ -409,6 +417,16 @@ func NewRollapp(
authtypes.NewModuleAddress(timeupgradetypes.ModuleName).String(),
)

app.DividendsKeeper = dividendskeeper.NewKeeper(
appCodec,
keys[dividendstypes.StoreKey],
app.StakingKeeper,
app.AccountKeeper,
app.DistrKeeper,
app.BankKeeper,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)

// register the staking hooks
// NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks
app.StakingKeeper = *stakingKeeper.SetHooks(app.DistrKeeper.Hooks())
Expand Down Expand Up @@ -459,7 +477,7 @@ func NewRollapp(

app.GovKeeper = *govKeeper.SetHooks(
govtypes.NewMultiGovHooks(
// register the governance hooks
// register the governance hooks
),
)

Expand Down Expand Up @@ -609,6 +627,7 @@ func NewRollapp(
hubgenesis.NewAppModule(appCodec, app.HubGenesisKeeper),
gasless.NewAppModule(appCodec, app.GaslessKeeper),
rollappparams.NewAppModule(appCodec, app.RollappParamsKeeper),
dividends.NewAppModule(app.DividendsKeeper),
}

app.mm = module.NewManager(modules...)
Expand Down Expand Up @@ -639,6 +658,7 @@ func NewRollapp(
tokenfactorytypes.ModuleName,
gaslesstypes.ModuleName,
rollappparamstypes.ModuleName,
dividendstypes.ModuleName,
}
app.mm.SetOrderBeginBlockers(beginBlockersList...)

Expand All @@ -663,6 +683,7 @@ func NewRollapp(
tokenfactorytypes.ModuleName,
gaslesstypes.ModuleName,
rollappparamstypes.ModuleName,
dividendstypes.ModuleName,
}
app.mm.SetOrderEndBlockers(endBlockersList...)

Expand Down Expand Up @@ -693,6 +714,7 @@ func NewRollapp(
tokenfactorytypes.ModuleName,
gaslesstypes.ModuleName,
rollappparamstypes.ModuleName,
dividendstypes.ModuleName,
}
app.mm.SetOrderInitGenesis(initGenesisList...)

Expand Down
4 changes: 3 additions & 1 deletion testutil/app/apptesting/apptesting.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
// Setup sets up basic environment for suite (App, Ctx, and test accounts)
func (s *KeeperTestHelper) Setup() {
s.App = utils.Setup(s.T(), false)
s.Ctx = s.App.BaseApp.NewContext(false, tmtypes.Header{Height: 1, ChainID: "osmosis-1", Time: time.Now().UTC()})
s.Ctx = s.App.BaseApp.NewContext(false, tmtypes.Header{Height: 1, ChainID: "dymension_1234-1", Time: time.Now().UTC()})

Check warning

Code scanning / CodeQL

Calling the system time Warning test

Calling the system time may be a possible source of non-determinism
s.QueryHelper = &baseapp.QueryServiceTestHelper{
GRPCQueryRouter: s.App.GRPCQueryRouter(),
Ctx: s.Ctx,
Expand Down Expand Up @@ -230,6 +230,8 @@
// AssertEventEmitted asserts that ctx's event manager has emitted the given number of events
// of the given type.
func (s *KeeperTestHelper) AssertEventEmitted(ctx sdk.Context, eventTypeExpected string, numEventsExpected int) {
s.T().Helper()

allEvents := ctx.EventManager().Events()
// filter out other events
actualEvents := make([]sdk.Event, 0)
Expand Down
Loading
Loading