Skip to content

Commit

Permalink
feat: pools module (#8)
Browse files Browse the repository at this point in the history
## Description

This PR adds the `x/pools` module, which allows to create and query
restaking pools.

Depends-On: #7
Closes: MILK-45

<!-- Add a description of the changes that this PR introduces and the
files that
are the most critical to review. -->

---

### Author Checklist

*All items are required. Please add a note to the item if the item is
not applicable and
please add links to any relevant follow up issues.*

I have...

- [x] included the correct [type
prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json)
in the PR title
- [ ] added `!` to the type prefix if API or client breaking change
- [x] targeted the correct branch (see [PR
Targeting](https://github.com/desmos-labs/desmos/blob/master/CONTRIBUTING.md#pr-targeting))
- [x] provided a link to the relevant issue or specification
- [x] followed the guidelines for [building
modules](https://docs.cosmos.network/v0.44/building-modules/intro.html)
- [x] included the necessary unit and integration
[tests](https://github.com/desmos-labs/desmos/blob/master/CONTRIBUTING.md#testing)
- [ ] added a changelog entry to `CHANGELOG.md`
- [x] included comments for [documenting Go
code](https://blog.golang.org/godoc)
- [ ] updated the relevant documentation or specification
- [x] reviewed "Files changed" and left comments if necessary
- [x] confirmed all CI checks have passed

### Reviewers Checklist

*All items are required. Please add a note if the item is not applicable
and please add
your handle next to the items reviewed if you only reviewed selected
items.*

I have...

- [ ] confirmed the correct [type
prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json)
in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)
  • Loading branch information
RiccardoM authored Jun 18, 2024
1 parent cbbe379 commit 6672cb2
Show file tree
Hide file tree
Showing 31 changed files with 4,301 additions and 2 deletions.
16 changes: 14 additions & 2 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ import (
"github.com/milkyway-labs/milkyway/x/operators"
operatorskeeper "github.com/milkyway-labs/milkyway/x/operators/keeper"
operatorstypes "github.com/milkyway-labs/milkyway/x/operators/types"
"github.com/milkyway-labs/milkyway/x/pools"
poolskeeper "github.com/milkyway-labs/milkyway/x/pools/keeper"
poolstypes "github.com/milkyway-labs/milkyway/x/pools/types"
"github.com/milkyway-labs/milkyway/x/records"
recordskeeper "github.com/milkyway-labs/milkyway/x/records/keeper"
recordstypes "github.com/milkyway-labs/milkyway/x/records/types"
Expand Down Expand Up @@ -284,6 +287,7 @@ type MilkyWayApp struct {

ServicesKeeper *serviceskeeper.Keeper
OperatorsKeeper *operatorskeeper.Keeper
PoolsKeeper *poolskeeper.Keeper

// make scoped keepers public for test purposes
ScopedIBCKeeper capabilitykeeper.ScopedKeeper
Expand Down Expand Up @@ -347,7 +351,7 @@ func NewMilkyWayApp(
icacallbackstypes.StoreKey, recordstypes.StoreKey, stakeibctypes.StoreKey,

// Custom modules
servicestypes.StoreKey, operatorstypes.StoreKey,
servicestypes.StoreKey, operatorstypes.StoreKey, poolstypes.StoreKey,
)
tkeys := storetypes.NewTransientStoreKeys(forwardingtypes.TransientStoreKey)
memKeys := storetypes.NewMemoryStoreKeys(capabilitytypes.MemStoreKey)
Expand Down Expand Up @@ -874,6 +878,11 @@ func NewMilkyWayApp(
communityPoolKeeper,
authorityAddr,
)
app.PoolsKeeper = poolskeeper.NewKeeper(
app.appCodec,
keys[poolstypes.StoreKey],
app.AccountKeeper,
)

/**** Module Options ****/

Expand Down Expand Up @@ -923,6 +932,7 @@ func NewMilkyWayApp(
// custom modules
services.NewAppModule(appCodec, app.ServicesKeeper),
operators.NewAppModule(appCodec, app.OperatorsKeeper),
pools.NewAppModule(appCodec, app.PoolsKeeper),
)

if err := app.setupIndexer(appOpts, homePath, ac, vc, appCodec); err != nil {
Expand Down Expand Up @@ -963,6 +973,7 @@ func NewMilkyWayApp(

servicestypes.ModuleName,
operatorstypes.ModuleName,
poolstypes.ModuleName,
)

app.ModuleManager.SetOrderEndBlockers(
Expand All @@ -979,6 +990,7 @@ func NewMilkyWayApp(

servicestypes.ModuleName,
operatorstypes.ModuleName,
poolstypes.ModuleName,
)

// NOTE: The genutils module must occur after staking so that pools are
Expand All @@ -998,7 +1010,7 @@ func NewMilkyWayApp(
stakeibctypes.ModuleName, epochstypes.ModuleName, icqtypes.ModuleName,
recordstypes.ModuleName, ratelimittypes.ModuleName, icacallbackstypes.ModuleName,

servicestypes.ModuleName, operatorstypes.ModuleName,
servicestypes.ModuleName, operatorstypes.ModuleName, poolstypes.ModuleName,
}

app.ModuleManager.SetOrderInitGenesis(genesisModuleOrder...)
Expand Down
22 changes: 22 additions & 0 deletions proto/milkyway/pools/v1/genesis.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
syntax = "proto3";
package milkyway.pools.v1;

import "gogoproto/gogo.proto";
import "google/protobuf/timestamp.proto";
import "milkyway/pools/v1/models.proto";

option go_package = "github.com/milkyway-labs/milkyway/x/pools/types";

// GenesisState defines the pools module's genesis state.
message GenesisState {

// NextPoolID represents the id to be used when creating the next pool.
uint32 next_pool_id = 1 [
(gogoproto.customname) = "NextPoolID",
(gogoproto.moretags) = "yaml:\"next_pool_id\""
];

// Operators defines the list of pools.
repeated Pool pools = 2
[ (gogoproto.moretags) = "yaml:\"pools\"", (gogoproto.nullable) = false ];
}
20 changes: 20 additions & 0 deletions proto/milkyway/pools/v1/models.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
syntax = "proto3";
package milkyway.pools.v1;

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

option go_package = "github.com/milkyway-labs/milkyway/x/pools/types";

// Pool defines the structure of a restaking pool
message Pool {
// ID is the auto-generated unique identifier for the pool
uint32 id = 1 [ (gogoproto.customname) = "ID" ];

// Denom represents the denomination of the tokens that are staked in the pool
string denom = 2;

// Address represents the address of the account that is associated with this
// pool. This will be used to store tokens that users delegate to this pool.
string address = 3 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
}
62 changes: 62 additions & 0 deletions proto/milkyway/pools/v1/query.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
syntax = "proto3";
package milkyway.pools.v1;

import "gogoproto/gogo.proto";
import "google/api/annotations.proto";
import "cosmos/base/query/v1beta1/pagination.proto";
import "milkyway/pools/v1/models.proto";

option go_package = "github.com/milkyway-labs/milkyway/x/pools/types";

// Query defines the gRPC querier service.
service Query {
// PoolById defines a gRPC query method that returns the pool by the given ID.
rpc PoolById(QueryPoolByIdRequest) returns (QueryPoolResponse) {
option (google.api.http).get = "/milkyway/pool/v1/pool/{pool_id}";
}

// PoolByDenom defines a gRPC query method that returns the pool by the given
// denom.
rpc PoolByDenom(QueryPoolByDenomRequest) returns (QueryPoolResponse) {
option (google.api.http).get = "/milkyway/pool/v1/pool/denom/{denom}";
}

// Pools defines a gRPC query method that returns all pools.
rpc Pools(QueryPoolsRequest) returns (QueryPoolsResponse) {
option (google.api.http).get = "/milkyway/pool/v1/pools";
}
}

// QueryPoolByIdRequest is the request type for the Query/PoolById RPC method.
message QueryPoolByIdRequest {
// PoolID is the ID of the pool to query
uint32 pool_id = 1;
}

// QueryPoolByDenomRequest is the request type for the Query/PollByDenom RPC
// method.
message QueryPoolByDenomRequest {
// Denom is the denom for which the pool is to be queried
string denom = 1;
}

// QueryOperatorRPoolsesponse is the response type for the Query/PoolById and
// Query/PoolByDenom RPC methods.
message QueryPoolResponse {
// Pool is the queried pool
Pool pool = 1 [ (gogoproto.nullable) = false ];
}

// QueryPoolsRequest is the request type for the Query/Pools RPC method.
message QueryPoolsRequest {
cosmos.base.query.v1beta1.PageRequest pagination = 1;
}

// QueryPoolsResponse is the response type for the Query/Pools RPC method.
message QueryPoolsResponse {
// Pools is the list of pool
repeated Pool pools = 1 [ (gogoproto.nullable) = false ];

// Pagination defines the pagination response
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}
22 changes: 22 additions & 0 deletions utils/slices.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,25 @@ func FindDuplicate[T any](slice []T, compare func(a T, b T) bool) *T {
}
return nil
}

// Map applies the given function to each element in the slice and returns a new slice with the results.
func Map[T, U any](slice []T, f func(T) U) []U {
result := make([]U, len(slice))
for i, v := range slice {
result[i] = f(v)
}
return result
}

// RemoveDuplicates removes all duplicate elements from the slice.
func RemoveDuplicates[T comparable](slice []T) []T {
seen := make(map[T]bool)
result := make([]T, 0, len(slice))
for _, v := range slice {
if _, ok := seen[v]; !ok {
seen[v] = true
result = append(result, v)
}
}
return result
}
126 changes: 126 additions & 0 deletions x/pools/client/cli/query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package cli

import (
"fmt"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/version"
"github.com/spf13/cobra"

"github.com/milkyway-labs/milkyway/x/pools/types"
)

// GetQueryCmd returns the command allowing to perform queries
func GetQueryCmd() *cobra.Command {
servicesQueryCmd := &cobra.Command{
Use: types.ModuleName,
Short: fmt.Sprintf("Querying commands for the %s module", types.ModuleName),
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
}

servicesQueryCmd.AddCommand(
getCmdQueryPoolByID(),
getCmdQueryPoolByDenom(),
getCmdQueryPools(),
)

return servicesQueryCmd
}

// getCmdQueryPoolByID returns the command allowing to query a service
func getCmdQueryPoolByID() *cobra.Command {
cmd := &cobra.Command{
Use: "pool [pool-id]",
Short: "Query the pool with the given id",
Example: fmt.Sprintf(`%s query %s pool 1`, version.AppName, types.ModuleName),
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)

poolID, err := types.ParsePoolID(args[0])
if err != nil {
return err
}

res, err := queryClient.PoolById(cmd.Context(), types.NewQueryPoolByIdRequest(poolID))
if err != nil {
return err
}

return clientCtx.PrintProto(res)
},
}

flags.AddQueryFlagsToCmd(cmd)

return cmd
}

// getCmdQueryPoolByDenom returns the command allowing to query services
func getCmdQueryPoolByDenom() *cobra.Command {
cmd := &cobra.Command{
Use: "pool-by-denom [denom]",
Short: "Query the pool associated with the given denom",
Example: fmt.Sprintf(`%s query %s pool umilk`, version.AppName, types.ModuleName),
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)

res, err := queryClient.PoolByDenom(cmd.Context(), types.NewQueryPoolByDenomRequest(args[0]))
if err != nil {
return err
}

return clientCtx.PrintProto(res)
},
}

flags.AddQueryFlagsToCmd(cmd)

return cmd
}

// getCmdQueryPools returns the command to query the stored pools
func getCmdQueryPools() *cobra.Command {
cmd := &cobra.Command{
Use: "pools",
Short: "Query the pools",
Example: fmt.Sprintf(`%s query %s pools --page=2 --limit=100`, version.AppName, types.ModuleName),
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)

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

res, err := queryClient.Pools(cmd.Context(), types.NewQueryPoolsRequest(pageReq))
if err != nil {
return err
}

return clientCtx.PrintProto(res)
},
}

flags.AddQueryFlagsToCmd(cmd)
flags.AddPaginationFlagsToCmd(cmd, "pools")

return cmd
}
Loading

0 comments on commit 6672cb2

Please sign in to comment.