diff --git a/proto/milkyway/operators/v1/models.proto b/proto/milkyway/operators/v1/models.proto index 905dfc217..53741c910 100644 --- a/proto/milkyway/operators/v1/models.proto +++ b/proto/milkyway/operators/v1/models.proto @@ -3,6 +3,7 @@ package milkyway.operators.v1; import "gogoproto/gogo.proto"; import "cosmos_proto/cosmos.proto"; +import "cosmos/base/v1beta1/coin.proto"; option go_package = "github.com/milkyway-labs/milkyway/x/operators/types"; @@ -49,4 +50,16 @@ message Operator { // Address is the address of the account associated to the operator. // This will be used to store tokens that are delegated to this operator. string address = 7 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + + // Tokens define the delegated tokens. + repeated cosmos.base.v1beta1.Coin tokens = 8 [ + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", + (gogoproto.nullable) = false + ]; + + // DelegatorShares define the total shares issued to an operator's delegators. + repeated cosmos.base.v1beta1.DecCoin delegator_shares = 9 [ + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins", + (gogoproto.nullable) = false + ]; } diff --git a/proto/milkyway/pools/v1/models.proto b/proto/milkyway/pools/v1/models.proto index cf20ec163..6664725ed 100644 --- a/proto/milkyway/pools/v1/models.proto +++ b/proto/milkyway/pools/v1/models.proto @@ -17,4 +17,18 @@ message Pool { // 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" ]; + + // Tokens define the delegated tokens. + string tokens = 4 [ + (cosmos_proto.scalar) = "cosmos.Int", + (gogoproto.customtype) = "cosmossdk.io/math.Int", + (gogoproto.nullable) = false + ]; + + // DelegatorShares defines total shares issued to a pool's delegators. + string delegator_shares = 5 [ + (cosmos_proto.scalar) = "cosmos.Dec", + (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", + (gogoproto.nullable) = false + ]; } diff --git a/proto/milkyway/restaking/v1/genesis.proto b/proto/milkyway/restaking/v1/genesis.proto new file mode 100644 index 000000000..d001dc13e --- /dev/null +++ b/proto/milkyway/restaking/v1/genesis.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; +package milkyway.restaking.v1; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "milkyway/restaking/v1/models.proto"; +import "milkyway/restaking/v1/params.proto"; + +option go_package = "github.com/milkyway-labs/milkyway/x/restaking/types"; + +// GenesisState defines the restaking module's genesis state. +message GenesisState { + // Params defines the parameters of the module. + Params params = 1 [ (gogoproto.nullable) = false ]; + + // PoolDelegations represents the delegations to pools. + repeated PoolDelegation pools_delegations = 2 + [ (gogoproto.nullable) = false ]; + + // ServiceDelegations represents the delegations to services. + repeated ServiceDelegation services_delegations = 3 + [ (gogoproto.nullable) = false ]; + + // OperatorDelegations represents the delegations to operators. + repeated OperatorDelegation operators_delegations = 4 + [ (gogoproto.nullable) = false ]; +} diff --git a/proto/milkyway/restaking/v1/messages.proto b/proto/milkyway/restaking/v1/messages.proto new file mode 100644 index 000000000..683483ef3 --- /dev/null +++ b/proto/milkyway/restaking/v1/messages.proto @@ -0,0 +1,124 @@ +syntax = "proto3"; +package milkyway.restaking.v1; + +import "amino/amino.proto"; +import "cosmos_proto/cosmos.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos/bank/v1beta1/bank.proto"; +import "cosmos/msg/v1/msg.proto"; +import "gogoproto/gogo.proto"; +import "milkyway/restaking/v1/params.proto"; + +option go_package = "github.com/milkyway-labs/milkyway/x/restaking/types"; + +// Msg defines the restaking module's gRPC message service. +service Msg { + option (cosmos.msg.v1.service) = true; + + // DelegatePool defines the operation that allows users to delegate any amount + // of an asset to a pool that can then be used to provide services with + // cryptoeconomic security. + rpc DelegatePool(MsgDelegatePool) returns (MsgDelegatePoolResponse); + + // DelegateOperator defines the operation that allows users to delegate their + // assets to a specific operator. + rpc DelegateOperator(MsgDelegateOperator) + returns (MsgDelegateOperatorResponse); + + // DelegateService defines the operation that allows users to delegate their + // assets to a specific service. + rpc DelegateService(MsgDelegateService) returns (MsgDelegateServiceResponse); + + // UpdateParams defines a (governance) operation for updating the module + // parameters. + // The authority defaults to the x/gov module account. + rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse); +} + +// MsgDelegatePool defines the message structure for the DelegatePool gRPC +// service method. It allows a user to put their assets into a restaking pool +// that will later be used to provide cryptoeconomic security to services that +// choose it. +message MsgDelegatePool { + option (cosmos.msg.v1.signer) = "delegator"; + option (amino.name) = "milkyway/MsgJoinRestakingPool"; + + // Delegator is the address of the user joining the pool + string delegator = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + + // Amount is the amount of coins to be staked + cosmos.base.v1beta1.Coin amount = 2 + [ (gogoproto.customname) = "Amount", (gogoproto.nullable) = false ]; +} + +// MsgDelegatePoolResponse defines the return value of MsgDelegatePool. +message MsgDelegatePoolResponse {} + +// MsgDelegateOperator defines the message structure for the OperatorRestake +// gRPC service method. It allows a user to delegate their assets to an +// operator. +message MsgDelegateOperator { + option (cosmos.msg.v1.signer) = "delegator"; + option (amino.name) = "milkyway/MsgDelegateOperator"; + + // Delegator is the address of the user delegating to the operator + string delegator = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + + // OperatorID is the ID of the operator to delegate to + uint32 operator_id = 2 [ (gogoproto.customname) = "OperatorID" ]; + + // Amount is the amount of coins to be delegated + repeated cosmos.base.v1beta1.Coin amount = 3 [ + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", + (gogoproto.customname) = "Amount", + (gogoproto.nullable) = false + ]; +} + +// MsgDelegateOperatorResponse is the return value of MsgDelegateOperator. +message MsgDelegateOperatorResponse {} + +// MsgDelegateService defines the message structure for the ServiceRestake gRPC +// service method. It allows a user to delegate their assets to a service. +message MsgDelegateService { + option (cosmos.msg.v1.signer) = "delegator"; + option (amino.name) = "milkyway/MsgDelegateService"; + + // Delegator is the address of the user delegating to the service + string delegator = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + + // ServiceID is the ID of the service to delegate to + uint32 service_id = 2 [ (gogoproto.customname) = "ServiceID" ]; + + // Amount is the amount of coins to be delegated + repeated cosmos.base.v1beta1.Coin amount = 3 [ + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", + (gogoproto.customname) = "Amount", + (gogoproto.nullable) = false + ]; +} + +// MsgDelegateServiceResponse is the return value of MsgDelegateService. +message MsgDelegateServiceResponse {} + +// MsgDeactivateService defines the message structure for the UpdateParams gRPC +// service method. It allows the authority to update the module parameters. +message MsgUpdateParams { + option (cosmos.msg.v1.signer) = "authority"; + option (amino.name) = "milkyway/x/restaking/MsgUpdateParams"; + + // Authority is the address that controls the module (defaults to x/gov unless + // overwritten). + string authority = 1 [ + (gogoproto.moretags) = "yaml:\"authority\"", + (cosmos_proto.scalar) = "cosmos.AddressString" + ]; + + // Params define the parameters to update. + // + // NOTE: All parameters must be supplied. + Params params = 2 [ (gogoproto.nullable) = false ]; +} + +// MsgDeactivateServiceResponse is the return value of MsgUpdateParams. +message MsgUpdateParamsResponse {} \ No newline at end of file diff --git a/proto/milkyway/restaking/v1/models.proto b/proto/milkyway/restaking/v1/models.proto new file mode 100644 index 000000000..753efd86e --- /dev/null +++ b/proto/milkyway/restaking/v1/models.proto @@ -0,0 +1,69 @@ +syntax = "proto3"; +package milkyway.restaking.v1; + +import "amino/amino.proto"; +import "cosmos_proto/cosmos.proto"; +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +option go_package = "github.com/milkyway-labs/milkyway/x/restaking/types"; + +// PoolDelegation represents the bond with tokens held by an account with a +// given pool. It is owned by one delegator, and is associated with a pool. +message PoolDelegation { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // UserAddress is the encoded address of the user. + string user_address = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + + // PoolID is the id of the pool. + uint32 pool_id = 2 [ (gogoproto.customname) = "PoolID" ]; + + // Shares define the delegation shares received. + string shares = 3 [ + (cosmos_proto.scalar) = "cosmos.Dec", + (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", + (gogoproto.nullable) = false + ]; +} + +// OperatorDelegation represents the bond with tokens held by an account with a +// given operator. It is owned by one delegator, and is associated with a +// operator. +message OperatorDelegation { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // UserAddress is the encoded address of the user. + string user_address = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + + // OperatorID is the id of the operator. + uint32 operator_id = 2 [ (gogoproto.customname) = "OperatorID" ]; + + // Shares define the delegation shares received. + repeated cosmos.base.v1beta1.DecCoin shares = 3 [ + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins", + (gogoproto.nullable) = false + ]; +} + +// ServiceDelegation represents the bond with tokens held by an account with a +// given service. It is owned by one delegator, and is associated with a +// service. +message ServiceDelegation { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // UserAddress is the encoded address of the user. + string user_address = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + + // ServiceID is the id of the service. + uint32 service_id = 2 [ (gogoproto.customname) = "ServiceID" ]; + + // Shares define the delegation shares received. + repeated cosmos.base.v1beta1.DecCoin shares = 3 [ + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins", + (gogoproto.nullable) = false + ]; +} \ No newline at end of file diff --git a/proto/milkyway/restaking/v1/params.proto b/proto/milkyway/restaking/v1/params.proto new file mode 100644 index 000000000..165178d7b --- /dev/null +++ b/proto/milkyway/restaking/v1/params.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; +package milkyway.restaking.v1; + +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +option go_package = "github.com/milkyway-labs/milkyway/x/restaking/types"; + +// Params defines the parameters for the module. +message Params { + + // UnbondingTime represents the time that will take for assets to be unbonded + // after the user initiates an unbonding request. This will be applied to all + // types of restaking: pool, operator and service restaking. + int64 unbonding_time = 1 [ (gogoproto.stdduration) = true ]; +} \ No newline at end of file diff --git a/proto/milkyway/services/v1/models.proto b/proto/milkyway/services/v1/models.proto index 14c3755f7..59347523b 100644 --- a/proto/milkyway/services/v1/models.proto +++ b/proto/milkyway/services/v1/models.proto @@ -3,6 +3,7 @@ package milkyway.services.v1; import "gogoproto/gogo.proto"; import "cosmos_proto/cosmos.proto"; +import "cosmos/base/v1beta1/coin.proto"; option go_package = "github.com/milkyway-labs/milkyway/x/services/types"; @@ -48,9 +49,20 @@ message Service { // PictureURL is the URL of the picture of the service string picture_url = 7 [ (gogoproto.customname) = "PictureURL" ]; - // Address is the address of the account associated with the service. // This will be used in order to store all the tokens that are delegated to // this service by various users. string address = 8 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + + // Tokens define the delegated tokens. + repeated cosmos.base.v1beta1.Coin tokens = 9 [ + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", + (gogoproto.nullable) = false + ]; + + // DelegatorShares define the total shares issued to an operator's delegators. + repeated cosmos.base.v1beta1.DecCoin delegator_shares = 10 [ + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins", + (gogoproto.nullable) = false + ]; } diff --git a/x/operators/types/errors.go b/x/operators/types/errors.go index ef4b505ce..6684402f7 100644 --- a/x/operators/types/errors.go +++ b/x/operators/types/errors.go @@ -8,4 +8,6 @@ var ( ErrInvalidGenesis = errors.Register(ModuleName, 1, "invalid genesis state") ErrInvalidDeactivationTime = errors.Register(ModuleName, 2, "invalid deactivation time") ErrOperatorNotFound = errors.Register(ModuleName, 3, "operator not found") + ErrOperatorNotActive = errors.Register(ModuleName, 4, "operator not active") + ErrInsufficientShares = errors.Register(ModuleName, 5, "insufficient delegation shares") ) diff --git a/x/operators/types/messages.pb.go b/x/operators/types/messages.pb.go index 5b5944267..0e499b4e9 100644 --- a/x/operators/types/messages.pb.go +++ b/x/operators/types/messages.pb.go @@ -488,52 +488,52 @@ func init() { } var fileDescriptor_376ca0aafe99868c = []byte{ - // 720 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x55, 0x3f, 0x6f, 0xd3, 0x4e, - 0x18, 0x8e, 0xd3, 0x3f, 0x3f, 0xf5, 0xf2, 0xeb, 0x3f, 0xd3, 0xaa, 0xa9, 0x4b, 0x6d, 0x64, 0x55, - 0xa8, 0x84, 0xd4, 0x6e, 0x5a, 0x16, 0xc2, 0x44, 0x54, 0x06, 0x10, 0x81, 0xca, 0xa8, 0x12, 0x62, - 0x89, 0x2e, 0xc9, 0xe1, 0x5a, 0x8d, 0x7d, 0xd1, 0xdd, 0x25, 0x69, 0x36, 0xc4, 0xc8, 0xc4, 0xc8, - 0x67, 0x60, 0xea, 0xc0, 0x27, 0x60, 0xa1, 0x63, 0xc5, 0xc4, 0x64, 0xa1, 0x74, 0xe8, 0x9e, 0x4f, - 0x80, 0xe2, 0x3b, 0xbb, 0x6e, 0xe2, 0x88, 0x16, 0x24, 0x96, 0x28, 0xef, 0xfb, 0x3c, 0xef, 0xbd, - 0xef, 0xf3, 0xbc, 0x3e, 0x1b, 0x6c, 0xb8, 0x4e, 0xe3, 0xa8, 0xdb, 0x81, 0x5d, 0x13, 0x37, 0x11, - 0x81, 0x0c, 0x13, 0x6a, 0xb6, 0x0b, 0xa6, 0x8b, 0x28, 0x85, 0x36, 0xa2, 0x46, 0x93, 0x60, 0x86, - 0xe5, 0xe5, 0x90, 0x65, 0x44, 0x2c, 0xa3, 0x5d, 0x50, 0x16, 0xa1, 0xeb, 0x78, 0xd8, 0x0c, 0x7e, - 0x39, 0x53, 0x59, 0xad, 0x61, 0xea, 0x62, 0x5a, 0x09, 0x22, 0x93, 0x07, 0x02, 0x52, 0x79, 0x64, - 0x56, 0x21, 0x45, 0x66, 0xbb, 0x50, 0x45, 0x0c, 0x16, 0xcc, 0x1a, 0x76, 0xbc, 0x11, 0xdc, 0x3b, - 0x8a, 0xf0, 0x41, 0x20, 0xf0, 0x15, 0x81, 0xbb, 0xd4, 0x0e, 0x46, 0xa4, 0xb6, 0x00, 0x96, 0x6c, - 0x6c, 0x63, 0xde, 0x70, 0xf0, 0x4f, 0x64, 0xf5, 0x64, 0x65, 0x4d, 0x48, 0xa0, 0x2b, 0x46, 0xd2, - 0x3f, 0xa5, 0xc1, 0xad, 0x32, 0xb5, 0x2d, 0x64, 0x3b, 0x94, 0x21, 0xf2, 0x52, 0x10, 0xe5, 0x6d, - 0x30, 0x4d, 0x91, 0x57, 0x47, 0x24, 0x2b, 0xdd, 0x91, 0x36, 0x67, 0x4a, 0xd9, 0xef, 0x5f, 0xb6, - 0x96, 0x84, 0x98, 0xc7, 0xf5, 0x3a, 0x41, 0x94, 0xbe, 0x62, 0xc4, 0xf1, 0x6c, 0x4b, 0xf0, 0xe4, - 0x3c, 0xf8, 0xcf, 0xc5, 0x9e, 0x73, 0x84, 0x48, 0x36, 0x1d, 0x94, 0xc8, 0x7d, 0x5f, 0x9b, 0xeb, - 0x42, 0xb7, 0x51, 0xd4, 0x05, 0xa0, 0x5b, 0x21, 0x65, 0xc0, 0xee, 0xa0, 0x2a, 0x75, 0x18, 0xca, - 0x4e, 0x0c, 0xb3, 0x05, 0xa0, 0x5b, 0x21, 0x45, 0x7e, 0x02, 0x32, 0x4d, 0xa7, 0xc6, 0x5a, 0x04, - 0x55, 0x5a, 0xa4, 0x91, 0x9d, 0x0c, 0x2a, 0x36, 0x7a, 0xbe, 0x06, 0xf6, 0x79, 0xfa, 0xc0, 0x7a, - 0xde, 0xf7, 0x35, 0x99, 0xd7, 0xc7, 0xa8, 0xba, 0x05, 0x44, 0x74, 0x40, 0x1a, 0xc5, 0xfc, 0xfb, - 0x8b, 0x93, 0x9c, 0x98, 0xf7, 0xc3, 0xc5, 0x49, 0xee, 0x76, 0x64, 0x50, 0x82, 0x05, 0xfa, 0x6b, - 0xb0, 0x96, 0x90, 0xb6, 0x10, 0x6d, 0x62, 0x8f, 0x22, 0xf9, 0x21, 0x98, 0xf7, 0x50, 0xa7, 0x12, - 0x5a, 0x5b, 0x71, 0xea, 0x81, 0x55, 0xb3, 0xa5, 0xc5, 0x9e, 0xaf, 0xcd, 0xbe, 0x40, 0x9d, 0xb0, - 0xe2, 0xe9, 0x9e, 0x35, 0xeb, 0xc5, 0xc2, 0xba, 0xfe, 0x35, 0x0d, 0x16, 0xcb, 0xd4, 0x3e, 0x68, - 0xd6, 0x21, 0x43, 0x7f, 0x61, 0xb9, 0x09, 0x32, 0xf1, 0xf6, 0xe9, 0xa0, 0xfd, 0xdc, 0xc0, 0x96, - 0x58, 0x6f, 0x80, 0xa3, 0xc6, 0xf2, 0xbd, 0xcb, 0x1d, 0x71, 0xd7, 0xe7, 0xfb, 0xbe, 0x96, 0xe1, - 0xae, 0x79, 0xd0, 0x45, 0xc9, 0x0b, 0x9a, 0xbc, 0xf1, 0x82, 0xa6, 0xfe, 0x70, 0x41, 0xb9, 0xa1, - 0x05, 0x29, 0xf1, 0x05, 0x5d, 0xb5, 0x4b, 0x5f, 0x03, 0xab, 0x23, 0xc9, 0x70, 0x39, 0xfa, 0x67, - 0x09, 0x2c, 0x97, 0xa9, 0xbd, 0x87, 0x60, 0x8d, 0x39, 0xed, 0x7f, 0xeb, 0x72, 0xd1, 0x18, 0x52, - 0xa1, 0xc6, 0x55, 0x8c, 0x8e, 0xa4, 0x6b, 0x60, 0x3d, 0x11, 0x88, 0xd4, 0x7c, 0x93, 0xc0, 0x7c, - 0xa4, 0x75, 0x3f, 0xb8, 0xbe, 0xf2, 0x33, 0x30, 0x03, 0x5b, 0xec, 0x10, 0x13, 0x87, 0x75, 0x85, - 0x94, 0x7c, 0xdf, 0xd7, 0x16, 0xb8, 0xc3, 0x11, 0xa4, 0x8f, 0x95, 0x77, 0x59, 0x2e, 0x3f, 0x02, - 0xd3, 0xfc, 0xa5, 0x10, 0x88, 0xcb, 0xec, 0xac, 0x1b, 0x89, 0x6f, 0x3b, 0x83, 0xb7, 0x2e, 0x4d, - 0x9e, 0xfa, 0x5a, 0xca, 0x12, 0x25, 0xc5, 0xfb, 0x03, 0xb5, 0x97, 0x87, 0x0d, 0x04, 0x67, 0x47, - 0xd7, 0xc6, 0x4b, 0xf5, 0x55, 0xb0, 0x32, 0x94, 0x0a, 0x45, 0xee, 0x9c, 0x4d, 0x80, 0x89, 0x32, - 0xb5, 0x65, 0x02, 0x16, 0x46, 0xde, 0x46, 0xb9, 0x31, 0x03, 0x25, 0xdc, 0x4f, 0x65, 0xe7, 0xfa, - 0xdc, 0xe8, 0x2e, 0x37, 0xc0, 0xdc, 0xd0, 0x65, 0xdc, 0x1c, 0x7f, 0xca, 0x55, 0xa6, 0xb2, 0x7d, - 0x5d, 0x66, 0xd4, 0xed, 0x18, 0xc8, 0x09, 0x0f, 0x66, 0x7e, 0xfc, 0x39, 0xa3, 0x6c, 0xe5, 0xc1, - 0x4d, 0xd8, 0x51, 0xe7, 0xb7, 0xe0, 0xff, 0x2b, 0x0f, 0xd1, 0xdd, 0xdf, 0xcd, 0xce, 0x79, 0x8a, - 0x71, 0x3d, 0x5e, 0xd8, 0x47, 0x99, 0x7a, 0x77, 0x71, 0x92, 0x93, 0x4a, 0xe5, 0xd3, 0x9e, 0x2a, - 0x9d, 0xf5, 0x54, 0xe9, 0x67, 0x4f, 0x95, 0x3e, 0x9e, 0xab, 0xa9, 0xb3, 0x73, 0x35, 0xf5, 0xe3, - 0x5c, 0x4d, 0xbd, 0xd9, 0xb5, 0x1d, 0x76, 0xd8, 0xaa, 0x1a, 0x35, 0xec, 0x9a, 0xe1, 0xd1, 0x5b, - 0x0d, 0x58, 0xa5, 0x51, 0x64, 0x1e, 0xc7, 0xbe, 0x5a, 0xac, 0xdb, 0x44, 0xb4, 0x3a, 0x1d, 0x7c, - 0xb2, 0x76, 0x7f, 0x05, 0x00, 0x00, 0xff, 0xff, 0xbb, 0xc1, 0xb6, 0xc4, 0xb2, 0x07, 0x00, 0x00, + // 718 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x55, 0x3d, 0x6f, 0xd3, 0x4e, + 0x1c, 0x8e, 0xd3, 0x97, 0xbf, 0x7a, 0xf9, 0xf7, 0xcd, 0xb4, 0x6a, 0x92, 0xaa, 0x36, 0xb2, 0x2a, + 0x54, 0x42, 0x6a, 0x37, 0x2d, 0x0b, 0x61, 0x22, 0x2a, 0x03, 0x88, 0x40, 0x65, 0x54, 0x09, 0xb1, + 0x44, 0x97, 0xe4, 0x70, 0xad, 0xc6, 0x3e, 0xeb, 0xee, 0x92, 0x34, 0x1b, 0x62, 0x64, 0x62, 0xe5, + 0x33, 0xb0, 0x74, 0xe0, 0x1b, 0xb0, 0x74, 0xac, 0x98, 0x98, 0x2c, 0x94, 0x0e, 0xdd, 0xf3, 0x09, + 0x50, 0x7c, 0x67, 0xe7, 0xcd, 0x11, 0x2d, 0x48, 0x2c, 0x51, 0x7e, 0xf7, 0x3c, 0xbf, 0x97, 0xe7, + 0xf9, 0xf9, 0x6c, 0xb0, 0xed, 0xd8, 0x8d, 0xd3, 0x4e, 0x1b, 0x76, 0x0c, 0xec, 0x21, 0x02, 0x19, + 0x26, 0xd4, 0x68, 0x15, 0x0c, 0x07, 0x51, 0x0a, 0x2d, 0x44, 0x75, 0x8f, 0x60, 0x86, 0xe5, 0xf5, + 0x90, 0xa5, 0x47, 0x2c, 0xbd, 0x55, 0xc8, 0xae, 0x42, 0xc7, 0x76, 0xb1, 0x11, 0xfc, 0x72, 0x66, + 0x36, 0x53, 0xc3, 0xd4, 0xc1, 0xb4, 0x12, 0x44, 0x06, 0x0f, 0x04, 0xa4, 0xf0, 0xc8, 0xa8, 0x42, + 0x8a, 0x8c, 0x56, 0xa1, 0x8a, 0x18, 0x2c, 0x18, 0x35, 0x6c, 0xbb, 0x13, 0xb8, 0x7b, 0x1a, 0xe1, + 0xfd, 0x40, 0xe0, 0x1b, 0x02, 0x77, 0xa8, 0x15, 0x8c, 0x48, 0x2d, 0x01, 0xac, 0x59, 0xd8, 0xc2, + 0xbc, 0x61, 0xff, 0x9f, 0x38, 0xd5, 0xe2, 0x95, 0x79, 0x90, 0x40, 0x47, 0x8c, 0xa4, 0x7d, 0x4e, + 0x82, 0x3b, 0x65, 0x6a, 0x99, 0xc8, 0xb2, 0x29, 0x43, 0xe4, 0x95, 0x20, 0xca, 0x7b, 0x60, 0x9e, + 0x22, 0xb7, 0x8e, 0x48, 0x5a, 0xba, 0x2b, 0xed, 0x2c, 0x94, 0xd2, 0xdf, 0xbf, 0xee, 0xae, 0x09, + 0x31, 0x4f, 0xea, 0x75, 0x82, 0x28, 0x7d, 0xcd, 0x88, 0xed, 0x5a, 0xa6, 0xe0, 0xc9, 0x79, 0xf0, + 0x9f, 0x83, 0x5d, 0xfb, 0x14, 0x91, 0x74, 0x32, 0x48, 0x91, 0x7b, 0xbe, 0xba, 0xd4, 0x81, 0x4e, + 0xa3, 0xa8, 0x09, 0x40, 0x33, 0x43, 0x4a, 0x9f, 0xdd, 0x46, 0x55, 0x6a, 0x33, 0x94, 0x9e, 0x19, + 0x67, 0x0b, 0x40, 0x33, 0x43, 0x8a, 0xfc, 0x14, 0xa4, 0x3c, 0xbb, 0xc6, 0x9a, 0x04, 0x55, 0x9a, + 0xa4, 0x91, 0x9e, 0x0d, 0x32, 0xb6, 0xbb, 0xbe, 0x0a, 0x8e, 0xf8, 0xf1, 0xb1, 0xf9, 0xa2, 0xe7, + 0xab, 0x32, 0xcf, 0x1f, 0xa2, 0x6a, 0x26, 0x10, 0xd1, 0x31, 0x69, 0x14, 0x77, 0x3f, 0x5c, 0x9f, + 0xe7, 0xc4, 0xbc, 0x1f, 0xaf, 0xcf, 0x73, 0x5b, 0x03, 0x5f, 0x62, 0x3c, 0xd0, 0xde, 0x80, 0xcd, + 0x98, 0x63, 0x13, 0x51, 0x0f, 0xbb, 0x14, 0xc9, 0x8f, 0xc0, 0xb2, 0x8b, 0xda, 0x95, 0xb0, 0x46, + 0xc5, 0xae, 0x07, 0x5e, 0x2d, 0x96, 0x56, 0xbb, 0xbe, 0xba, 0xf8, 0x12, 0xb5, 0xc3, 0x8c, 0x67, + 0x87, 0xe6, 0xa2, 0x3b, 0x14, 0xd6, 0xb5, 0x6f, 0x49, 0xb0, 0x5a, 0xa6, 0xd6, 0xb1, 0x57, 0x87, + 0x0c, 0xfd, 0x85, 0xe7, 0x06, 0x48, 0x0d, 0xb7, 0x4f, 0x06, 0xed, 0x97, 0xfa, 0xbe, 0x0c, 0xf5, + 0x06, 0x38, 0x6a, 0x2c, 0xdf, 0x1f, 0x2c, 0x89, 0xdb, 0xbe, 0xdc, 0xf3, 0xd5, 0x14, 0xb7, 0xcd, + 0x85, 0x0e, 0x8a, 0xdf, 0xd0, 0xec, 0xad, 0x37, 0x34, 0xf7, 0x87, 0x1b, 0x7a, 0x30, 0xb6, 0xa1, + 0xcd, 0x91, 0x0d, 0x8d, 0xfa, 0xa5, 0x6d, 0x82, 0xcc, 0xc4, 0x61, 0xb8, 0x1d, 0xed, 0x8b, 0x04, + 0xd6, 0xcb, 0xd4, 0x3a, 0x44, 0xb0, 0xc6, 0xec, 0xd6, 0xbf, 0xb5, 0xb9, 0x68, 0x8c, 0xc9, 0x50, + 0x47, 0x64, 0x4c, 0xce, 0xa4, 0xa9, 0x60, 0x2b, 0x16, 0x88, 0xe4, 0x5c, 0x48, 0x60, 0x39, 0x12, + 0x7b, 0x14, 0xdc, 0x60, 0xf9, 0x39, 0x58, 0x80, 0x4d, 0x76, 0x82, 0x89, 0xcd, 0x3a, 0x42, 0x4b, + 0xbe, 0xe7, 0xab, 0x2b, 0xdc, 0xe3, 0x08, 0xd2, 0xa6, 0xea, 0x1b, 0xa4, 0xcb, 0x8f, 0xc1, 0x3c, + 0x7f, 0x2f, 0x04, 0xea, 0x52, 0xfb, 0x5b, 0x7a, 0xec, 0x0b, 0x4f, 0xe7, 0xad, 0x4b, 0xb3, 0x17, + 0xbe, 0x9a, 0x30, 0x45, 0x4a, 0x31, 0xdf, 0x97, 0x3b, 0x28, 0xd6, 0x57, 0x9c, 0x89, 0x59, 0x1c, + 0xcf, 0xd5, 0x32, 0x60, 0x63, 0xec, 0x28, 0x54, 0xb9, 0x7f, 0x39, 0x03, 0x66, 0xca, 0xd4, 0x92, + 0x09, 0x58, 0x99, 0x78, 0x23, 0xe5, 0xa6, 0x4c, 0x14, 0x73, 0x45, 0xb3, 0xfb, 0x37, 0xe7, 0x46, + 0xd7, 0xb9, 0x01, 0x96, 0xc6, 0xee, 0xe3, 0xce, 0xf4, 0x2a, 0xa3, 0xcc, 0xec, 0xde, 0x4d, 0x99, + 0x51, 0xb7, 0x33, 0x20, 0xc7, 0x3c, 0x9a, 0xf9, 0xe9, 0x75, 0x26, 0xd9, 0xd9, 0x87, 0xb7, 0x61, + 0x47, 0x9d, 0xdf, 0x81, 0xff, 0x47, 0x9e, 0xa2, 0x7b, 0xbf, 0x9b, 0x9d, 0xf3, 0xb2, 0xfa, 0xcd, + 0x78, 0x61, 0x9f, 0xec, 0xdc, 0xfb, 0xeb, 0xf3, 0x9c, 0x54, 0x2a, 0x5f, 0x74, 0x15, 0xe9, 0xb2, + 0xab, 0x48, 0x3f, 0xbb, 0x8a, 0xf4, 0xe9, 0x4a, 0x49, 0x5c, 0x5e, 0x29, 0x89, 0x1f, 0x57, 0x4a, + 0xe2, 0xed, 0x81, 0x65, 0xb3, 0x93, 0x66, 0x55, 0xaf, 0x61, 0xc7, 0x08, 0x4b, 0xef, 0x36, 0x60, + 0x95, 0x46, 0x91, 0x71, 0x36, 0xf4, 0xe5, 0x62, 0x1d, 0x0f, 0xd1, 0xea, 0x7c, 0xf0, 0xd9, 0x3a, + 0xf8, 0x15, 0x00, 0x00, 0xff, 0xff, 0xd9, 0xd0, 0x71, 0xf1, 0xb6, 0x07, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/x/operators/types/models.go b/x/operators/types/models.go index 18ddd2615..93dce306f 100644 --- a/x/operators/types/models.go +++ b/x/operators/types/models.go @@ -5,6 +5,7 @@ import ( "strconv" "strings" + sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" ) @@ -37,10 +38,10 @@ func NewOperator( return Operator{ ID: id, Status: status, + Admin: admin, Moniker: moniker, Website: website, PictureURL: pictureURL, - Admin: admin, Address: GetOperatorAddress(id).String(), } } @@ -72,6 +73,73 @@ func (o *Operator) Validate() error { return nil } +// GetSharesDenom returns the shares denom for an operator and token denom +func (o Operator) GetSharesDenom(tokenDenom string) string { + return fmt.Sprintf("operator/%d/%s", o.ID, tokenDenom) +} + +// IsActive returns whether the operator is active. +func (o Operator) IsActive() bool { + return o.Status == OPERATOR_STATUS_ACTIVE +} + +// InvalidExRate returns whether the exchange rates is invalid. +// This can happen e.g. if Operator loses all tokens due to slashing. In this case, +// make all future delegations invalid. +func (o Operator) InvalidExRate() bool { + for _, token := range o.Tokens { + if token.IsZero() && o.DelegatorShares.AmountOf(token.Denom).IsPositive() { + return true + } + } + return false +} + +// SharesFromTokens returns the shares of a delegation given a bond amount. It +// returns an error if the operator has no tokens. +func (o Operator) SharesFromTokens(tokens sdk.Coin) (sdkmath.LegacyDec, error) { + if o.Tokens.IsZero() { + return sdkmath.LegacyZeroDec(), ErrInsufficientShares + } + + sharesDenom := o.GetSharesDenom(tokens.Denom) + delegatorTokenShares := o.DelegatorShares.AmountOf(sharesDenom) + operatorTokenAmount := o.Tokens.AmountOf(tokens.Denom) + + return delegatorTokenShares.MulInt(tokens.Amount).QuoInt(operatorTokenAmount), nil +} + +// AddTokensFromDelegation adds the given amount of tokens to the operator's total tokens, +// also updating the operator's delegator shares. +// It returns the updated operator and the shares issued. +func (o Operator) AddTokensFromDelegation(amount sdk.Coins) (Operator, sdk.DecCoins) { + // calculate the shares to issue + issuedShares := sdk.NewDecCoins() + for _, token := range amount { + var tokenShares sdk.DecCoin + sharesDenom := o.GetSharesDenom(token.Denom) + + delegatorShares := o.DelegatorShares.AmountOf(sharesDenom) + if delegatorShares.IsZero() { + // The first delegation to an operator sets the exchange rate to one + tokenShares = sdk.NewDecCoin(sharesDenom, token.Amount) + } else { + shares, err := o.SharesFromTokens(token) + if err != nil { + panic(err) + } + tokenShares = sdk.NewDecCoinFromDec(sharesDenom, shares) + } + + issuedShares = issuedShares.Add(tokenShares) + } + + o.Tokens = o.Tokens.Add(amount...) + o.DelegatorShares = o.DelegatorShares.Add(issuedShares...) + + return o, issuedShares +} + // -------------------------------------------------------------------------------------------------------------------- // OperatorUpdate defines the fields that can be updated in an Operator. diff --git a/x/operators/types/models.pb.go b/x/operators/types/models.pb.go index 84af9ddb7..1bc674cd3 100644 --- a/x/operators/types/models.pb.go +++ b/x/operators/types/models.pb.go @@ -6,6 +6,8 @@ package types import ( fmt "fmt" _ "github.com/cosmos/cosmos-proto" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" io "io" @@ -80,6 +82,10 @@ type Operator struct { // Address is the address of the account associated to the operator. // This will be used to store tokens that are delegated to this operator. Address string `protobuf:"bytes,7,opt,name=address,proto3" json:"address,omitempty"` + // Tokens define the delegated tokens. + Tokens github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,8,rep,name=tokens,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"tokens"` + // DelegatorShares define the total shares issued to an operator's delegators. + DelegatorShares github_com_cosmos_cosmos_sdk_types.DecCoins `protobuf:"bytes,9,rep,name=delegator_shares,json=delegatorShares,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.DecCoins" json:"delegator_shares"` } func (m *Operator) Reset() { *m = Operator{} } @@ -164,6 +170,20 @@ func (m *Operator) GetAddress() string { return "" } +func (m *Operator) GetTokens() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.Tokens + } + return nil +} + +func (m *Operator) GetDelegatorShares() github_com_cosmos_cosmos_sdk_types.DecCoins { + if m != nil { + return m.DelegatorShares + } + return nil +} + func init() { proto.RegisterEnum("milkyway.operators.v1.OperatorStatus", OperatorStatus_name, OperatorStatus_value) proto.RegisterType((*Operator)(nil), "milkyway.operators.v1.Operator") @@ -174,34 +194,42 @@ func init() { } var fileDescriptor_8fb40b1089c56c16 = []byte{ - // 432 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x92, 0xc1, 0x6e, 0xd3, 0x30, - 0x18, 0xc7, 0xe3, 0x6c, 0x6b, 0xc1, 0x88, 0xaa, 0xb2, 0xc6, 0x64, 0xca, 0x94, 0x56, 0x93, 0x90, - 0x2a, 0xa4, 0x25, 0xda, 0x76, 0xe6, 0x90, 0x6e, 0x01, 0x45, 0x82, 0xb6, 0x72, 0x52, 0x0e, 0x5c, - 0xaa, 0xb4, 0xb1, 0x8a, 0xb5, 0xa4, 0x8e, 0x6c, 0x67, 0xa3, 0x6f, 0xc0, 0x11, 0x89, 0x47, 0xe0, - 0xc6, 0x99, 0x87, 0xe0, 0x38, 0x71, 0xe2, 0x34, 0xa1, 0xf4, 0x45, 0x50, 0xe3, 0x64, 0x82, 0x0a, - 0xc4, 0xcd, 0x7f, 0xff, 0x7f, 0xdf, 0xf7, 0xff, 0x2c, 0x7f, 0xf0, 0x28, 0x65, 0xc9, 0xe5, 0xea, - 0x3a, 0x5a, 0x39, 0x3c, 0xa3, 0x22, 0x52, 0x5c, 0x48, 0xe7, 0xea, 0xc4, 0x49, 0x79, 0x4c, 0x13, - 0x69, 0x67, 0x82, 0x2b, 0x8e, 0x1e, 0xd5, 0x8c, 0x7d, 0xc7, 0xd8, 0x57, 0x27, 0x9d, 0xfd, 0x05, - 0x5f, 0xf0, 0x92, 0x70, 0x36, 0x27, 0x0d, 0x77, 0x1e, 0xcf, 0xb9, 0x4c, 0xb9, 0x9c, 0x6a, 0x43, - 0x0b, 0x6d, 0x1d, 0x7d, 0x31, 0xe1, 0xbd, 0x51, 0xd5, 0x01, 0x1d, 0x40, 0x93, 0xc5, 0x18, 0xf4, - 0x40, 0xff, 0xe1, 0xa0, 0x51, 0xdc, 0x76, 0x4d, 0xff, 0x82, 0x98, 0x2c, 0x46, 0xcf, 0x61, 0x43, - 0xaa, 0x48, 0xe5, 0x12, 0x9b, 0x3d, 0xd0, 0x6f, 0x9d, 0x3e, 0xb5, 0xff, 0x9a, 0x6e, 0xd7, 0x8d, - 0x82, 0x12, 0x26, 0x55, 0x11, 0xb2, 0xe1, 0x5e, 0x14, 0xa7, 0x6c, 0x89, 0x77, 0x7a, 0xa0, 0x7f, - 0x7f, 0x80, 0xbf, 0x7f, 0x3d, 0xde, 0xaf, 0x86, 0x70, 0xe3, 0x58, 0x50, 0x29, 0x03, 0x25, 0xd8, - 0x72, 0x41, 0x34, 0x86, 0x30, 0x6c, 0xa6, 0x7c, 0xc9, 0x2e, 0xa9, 0xc0, 0xbb, 0x9b, 0x0a, 0x52, - 0xcb, 0x8d, 0x73, 0x4d, 0x67, 0x92, 0x29, 0x8a, 0xf7, 0xb4, 0x53, 0x49, 0xe4, 0xc0, 0x07, 0x19, - 0x9b, 0xab, 0x5c, 0xd0, 0x69, 0x2e, 0x12, 0xdc, 0x28, 0x93, 0x5a, 0xc5, 0x6d, 0x17, 0x8e, 0xf5, - 0xf5, 0x84, 0xbc, 0x22, 0xb0, 0x42, 0x26, 0x22, 0x41, 0xa7, 0xb0, 0x19, 0xe9, 0x70, 0xdc, 0xfc, - 0xcf, 0x58, 0x35, 0xf8, 0xec, 0x13, 0x80, 0xad, 0x3f, 0xdf, 0x88, 0xba, 0xf0, 0xc9, 0x68, 0xec, - 0x11, 0x37, 0x1c, 0x91, 0x69, 0x10, 0xba, 0xe1, 0x24, 0x98, 0x4e, 0x86, 0xc1, 0xd8, 0x3b, 0xf7, - 0x5f, 0xf8, 0xde, 0x45, 0xdb, 0x40, 0x1d, 0x78, 0xb0, 0x0d, 0xb8, 0xe7, 0xa1, 0xff, 0xc6, 0x6b, - 0x03, 0xd4, 0x83, 0x87, 0xdb, 0x9e, 0x3f, 0x2c, 0x5d, 0x37, 0xf4, 0x87, 0x2f, 0xdb, 0x26, 0x3a, - 0x84, 0xf8, 0x1f, 0x84, 0xd7, 0xde, 0xe9, 0xec, 0x7e, 0xf8, 0x6c, 0x19, 0x83, 0xd7, 0xdf, 0x0a, - 0x0b, 0xdc, 0x14, 0x16, 0xf8, 0x59, 0x58, 0xe0, 0xe3, 0xda, 0x32, 0x6e, 0xd6, 0x96, 0xf1, 0x63, - 0x6d, 0x19, 0x6f, 0xcf, 0x16, 0x4c, 0xbd, 0xcb, 0x67, 0xf6, 0x9c, 0xa7, 0x4e, 0xfd, 0x63, 0xc7, - 0x49, 0x34, 0x93, 0x77, 0xca, 0x79, 0xff, 0xdb, 0x8e, 0xa9, 0x55, 0x46, 0xe5, 0xac, 0x51, 0x2e, - 0xc6, 0xd9, 0xaf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7b, 0xe3, 0xe9, 0x28, 0x86, 0x02, 0x00, 0x00, + // 545 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0x4f, 0x8f, 0xd2, 0x40, + 0x18, 0xc6, 0x5b, 0x60, 0x61, 0x77, 0x36, 0x22, 0x99, 0xac, 0x9b, 0x59, 0x24, 0x85, 0x6c, 0x62, + 0x42, 0x34, 0xb4, 0x02, 0x67, 0x0f, 0xfc, 0xd3, 0x34, 0x51, 0x20, 0x2d, 0x78, 0xf0, 0x42, 0xfa, + 0x67, 0xc2, 0x4e, 0x68, 0x3b, 0xa4, 0x33, 0xb0, 0x92, 0xf8, 0x01, 0xbc, 0x98, 0x98, 0xf8, 0x11, + 0xbc, 0x79, 0xf6, 0x43, 0xec, 0x71, 0xe3, 0xc9, 0x13, 0x1a, 0xf8, 0x22, 0x86, 0x76, 0x4a, 0xd6, + 0xcd, 0x1a, 0xf7, 0xd4, 0x3e, 0x7d, 0x7e, 0xef, 0x3c, 0xef, 0xbc, 0x33, 0x05, 0xe7, 0x3e, 0xf1, + 0x66, 0xab, 0x4b, 0x6b, 0xa5, 0xd1, 0x39, 0x0e, 0x2d, 0x4e, 0x43, 0xa6, 0x2d, 0xeb, 0x9a, 0x4f, + 0x5d, 0xec, 0x31, 0x75, 0x1e, 0x52, 0x4e, 0xe1, 0xa3, 0x84, 0x51, 0xf7, 0x8c, 0xba, 0xac, 0x17, + 0x4f, 0xa6, 0x74, 0x4a, 0x23, 0x42, 0xdb, 0xbd, 0xc5, 0x70, 0xf1, 0xcc, 0xa1, 0xcc, 0xa7, 0x6c, + 0x12, 0x1b, 0xb1, 0x10, 0x96, 0x12, 0x2b, 0xcd, 0xb6, 0x18, 0xd6, 0x96, 0x75, 0x1b, 0x73, 0xab, + 0xae, 0x39, 0x94, 0x04, 0xb1, 0x7f, 0xfe, 0x29, 0x03, 0x0e, 0x07, 0x22, 0x01, 0x9e, 0x82, 0x14, + 0x71, 0x91, 0x5c, 0x91, 0xab, 0x0f, 0xda, 0xd9, 0xcd, 0xba, 0x9c, 0xd2, 0xbb, 0x46, 0x8a, 0xb8, + 0xf0, 0x05, 0xc8, 0x32, 0x6e, 0xf1, 0x05, 0x43, 0xa9, 0x8a, 0x5c, 0xcd, 0x37, 0x9e, 0xa8, 0x77, + 0x76, 0xa7, 0x26, 0x0b, 0x99, 0x11, 0x6c, 0x88, 0x22, 0xa8, 0x82, 0x03, 0xcb, 0xf5, 0x49, 0x80, + 0xd2, 0x15, 0xb9, 0x7a, 0xd4, 0x46, 0x3f, 0xbe, 0xd7, 0x4e, 0x44, 0x93, 0x2d, 0xd7, 0x0d, 0x31, + 0x63, 0x26, 0x0f, 0x49, 0x30, 0x35, 0x62, 0x0c, 0x22, 0x90, 0xf3, 0x69, 0x40, 0x66, 0x38, 0x44, + 0x99, 0x5d, 0x85, 0x91, 0xc8, 0x9d, 0x73, 0x89, 0x6d, 0x46, 0x38, 0x46, 0x07, 0xb1, 0x23, 0x24, + 0xd4, 0xc0, 0xf1, 0x9c, 0x38, 0x7c, 0x11, 0xe2, 0xc9, 0x22, 0xf4, 0x50, 0x36, 0x4a, 0xca, 0x6f, + 0xd6, 0x65, 0x30, 0x8c, 0x3f, 0x8f, 0x8d, 0xd7, 0x06, 0x10, 0xc8, 0x38, 0xf4, 0x60, 0x03, 0xe4, + 0xac, 0x38, 0x1c, 0xe5, 0xfe, 0xd3, 0x56, 0x02, 0x42, 0x07, 0x64, 0x39, 0x9d, 0xe1, 0x80, 0xa1, + 0xc3, 0x4a, 0xba, 0x7a, 0xdc, 0x38, 0x53, 0x05, 0xbf, 0x9b, 0xae, 0x2a, 0xa6, 0xab, 0x76, 0x28, + 0x09, 0xda, 0xcf, 0xaf, 0xd6, 0x65, 0xe9, 0xdb, 0xaf, 0x72, 0x75, 0x4a, 0xf8, 0xc5, 0xc2, 0x56, + 0x1d, 0xea, 0x8b, 0x83, 0x11, 0x8f, 0x1a, 0x73, 0x67, 0x1a, 0x5f, 0xcd, 0x31, 0x8b, 0x0a, 0x98, + 0x21, 0x96, 0x86, 0x1f, 0x40, 0xc1, 0xc5, 0x1e, 0x9e, 0xee, 0x06, 0x39, 0x61, 0x17, 0x56, 0x88, + 0x19, 0x3a, 0x8a, 0xe2, 0x4a, 0x77, 0xc6, 0x75, 0xb1, 0x13, 0x25, 0x36, 0x45, 0xe2, 0xb3, 0x7b, + 0x24, 0x8a, 0x1a, 0x66, 0x3c, 0xdc, 0x47, 0x99, 0x51, 0xd2, 0xd3, 0x2f, 0x32, 0xc8, 0xff, 0x7d, + 0x8c, 0xb0, 0x0c, 0x1e, 0x0f, 0x86, 0x3d, 0xa3, 0x35, 0x1a, 0x18, 0x13, 0x73, 0xd4, 0x1a, 0x8d, + 0xcd, 0xc9, 0xb8, 0x6f, 0x0e, 0x7b, 0x1d, 0xfd, 0xa5, 0xde, 0xeb, 0x16, 0x24, 0x58, 0x04, 0xa7, + 0xb7, 0x81, 0x56, 0x67, 0xa4, 0xbf, 0xed, 0x15, 0x64, 0x58, 0x01, 0xa5, 0xdb, 0x9e, 0xde, 0x8f, + 0xdc, 0xd6, 0x48, 0xef, 0xbf, 0x2a, 0xa4, 0x60, 0x09, 0xa0, 0x7f, 0x10, 0xbd, 0x42, 0xba, 0x98, + 0xf9, 0xf8, 0x55, 0x91, 0xda, 0x6f, 0xae, 0x36, 0x8a, 0x7c, 0xbd, 0x51, 0xe4, 0xdf, 0x1b, 0x45, + 0xfe, 0xbc, 0x55, 0xa4, 0xeb, 0xad, 0x22, 0xfd, 0xdc, 0x2a, 0xd2, 0xbb, 0xe6, 0x8d, 0xdd, 0x26, + 0x97, 0xb2, 0xe6, 0x59, 0x36, 0xdb, 0x2b, 0xed, 0xfd, 0x8d, 0xdf, 0x2c, 0xda, 0xbe, 0x9d, 0x8d, + 0xee, 0x7e, 0xf3, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa1, 0x74, 0xda, 0x3b, 0x89, 0x03, 0x00, + 0x00, } func (m *Operator) Marshal() (dAtA []byte, err error) { @@ -224,6 +252,34 @@ func (m *Operator) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.DelegatorShares) > 0 { + for iNdEx := len(m.DelegatorShares) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.DelegatorShares[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintModels(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + } + if len(m.Tokens) > 0 { + for iNdEx := len(m.Tokens) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Tokens[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintModels(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + } if len(m.Address) > 0 { i -= len(m.Address) copy(dAtA[i:], m.Address) @@ -315,6 +371,18 @@ func (m *Operator) Size() (n int) { if l > 0 { n += 1 + l + sovModels(uint64(l)) } + if len(m.Tokens) > 0 { + for _, e := range m.Tokens { + l = e.Size() + n += 1 + l + sovModels(uint64(l)) + } + } + if len(m.DelegatorShares) > 0 { + for _, e := range m.DelegatorShares { + l = e.Size() + n += 1 + l + sovModels(uint64(l)) + } + } return n } @@ -551,6 +619,74 @@ func (m *Operator) Unmarshal(dAtA []byte) error { } m.Address = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Tokens", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthModels + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthModels + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Tokens = append(m.Tokens, types.Coin{}) + if err := m.Tokens[len(m.Tokens)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorShares", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthModels + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthModels + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DelegatorShares = append(m.DelegatorShares, types.DecCoin{}) + if err := m.DelegatorShares[len(m.DelegatorShares)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipModels(dAtA[iNdEx:]) diff --git a/x/pools/types/errors.go b/x/pools/types/errors.go index ab08baa2d..7bf8a20f9 100644 --- a/x/pools/types/errors.go +++ b/x/pools/types/errors.go @@ -5,5 +5,6 @@ import ( ) var ( - ErrInvalidGenesis = errors.Register(ModuleName, 1, "invalid genesis state") + ErrInvalidGenesis = errors.Register(ModuleName, 1, "invalid genesis state") + ErrInsufficientShares = errors.Register(ModuleName, 2, "insufficient delegation shares") ) diff --git a/x/pools/types/models.go b/x/pools/types/models.go index f2f98181a..a8699e3e0 100644 --- a/x/pools/types/models.go +++ b/x/pools/types/models.go @@ -4,6 +4,7 @@ import ( "fmt" "strconv" + sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" ) @@ -27,14 +28,16 @@ func ParsePoolID(value string) (uint32, error) { // NewPool creates a new Pool instance func NewPool(id uint32, denom string) Pool { return Pool{ - ID: id, - Denom: denom, - Address: GetPoolAddress(id).String(), + ID: id, + Denom: denom, + Address: GetPoolAddress(id).String(), + Tokens: sdkmath.ZeroInt(), + DelegatorShares: sdkmath.LegacyZeroDec(), } } // Validate checks if the pool is valid -func (p *Pool) Validate() error { +func (p Pool) Validate() error { if p.ID == 0 { return fmt.Errorf("invalid pool id") } @@ -50,3 +53,49 @@ func (p *Pool) Validate() error { return nil } + +// GetSharesDenom returns the shares denom for a pool and token denom +func (p Pool) GetSharesDenom(tokenDenom string) string { + return fmt.Sprintf("pool/%d/%s", p.ID, tokenDenom) +} + +// InvalidExRate returns whether the exchange rates is invalid. +// This can happen e.g. if Pool loses all tokens due to slashing. In this case, +// make all future delegations invalid. +func (p Pool) InvalidExRate() bool { + return p.Tokens.IsZero() && p.DelegatorShares.IsPositive() +} + +// SharesFromTokens returns the shares of a delegation given a bond amount. It +// returns an error if the pool has no tokens. +func (p Pool) SharesFromTokens(amt sdkmath.Int) (sdkmath.LegacyDec, error) { + if p.Tokens.IsZero() { + return sdkmath.LegacyZeroDec(), ErrInsufficientShares + } + + return p.DelegatorShares.MulInt(amt).QuoInt(p.Tokens), nil +} + +// AddTokensFromDelegation adds the given amount of tokens to the pool's total tokens, +// also updating the pool's delegator shares. +// It returns the updated pool and the shares issued. +func (p Pool) AddTokensFromDelegation(amount sdkmath.Int) (Pool, sdkmath.LegacyDec) { + // calculate the shares to issue + var issuedShares sdkmath.LegacyDec + if p.DelegatorShares.IsZero() { + // the first delegation to a validator sets the exchange rate to one + issuedShares = sdkmath.LegacyNewDecFromInt(amount) + } else { + shares, err := p.SharesFromTokens(amount) + if err != nil { + panic(err) + } + + issuedShares = shares + } + + p.Tokens = p.Tokens.Add(amount) + p.DelegatorShares = p.DelegatorShares.Add(issuedShares) + + return p, issuedShares +} diff --git a/x/pools/types/models.pb.go b/x/pools/types/models.pb.go index cb329e852..5991bcf43 100644 --- a/x/pools/types/models.pb.go +++ b/x/pools/types/models.pb.go @@ -4,6 +4,7 @@ package types import ( + cosmossdk_io_math "cosmossdk.io/math" fmt "fmt" _ "github.com/cosmos/cosmos-proto" _ "github.com/cosmos/gogoproto/gogoproto" @@ -33,6 +34,10 @@ type Pool struct { // 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. Address string `protobuf:"bytes,3,opt,name=address,proto3" json:"address,omitempty"` + // Tokens define the delegated tokens. + Tokens cosmossdk_io_math.Int `protobuf:"bytes,4,opt,name=tokens,proto3,customtype=cosmossdk.io/math.Int" json:"tokens"` + // DelegatorShares defines total shares issued to a pool's delegators. + DelegatorShares cosmossdk_io_math.LegacyDec `protobuf:"bytes,5,opt,name=delegator_shares,json=delegatorShares,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"delegator_shares"` } func (m *Pool) Reset() { *m = Pool{} } @@ -96,22 +101,28 @@ func init() { func init() { proto.RegisterFile("milkyway/pools/v1/models.proto", fileDescriptor_b29c6df2abe9a338) } var fileDescriptor_b29c6df2abe9a338 = []byte{ - // 242 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xcb, 0xcd, 0xcc, 0xc9, - 0xae, 0x2c, 0x4f, 0xac, 0xd4, 0x2f, 0xc8, 0xcf, 0xcf, 0x29, 0xd6, 0x2f, 0x33, 0xd4, 0xcf, 0xcd, - 0x4f, 0x49, 0xcd, 0x29, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x84, 0xc9, 0xeb, 0x81, - 0xe5, 0xf5, 0xca, 0x0c, 0xa5, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0xb2, 0xfa, 0x20, 0x16, 0x44, - 0xa1, 0x94, 0x64, 0x72, 0x7e, 0x71, 0x6e, 0x7e, 0x71, 0x3c, 0x44, 0x02, 0xc2, 0x81, 0x48, 0x29, - 0x65, 0x70, 0xb1, 0x04, 0xe4, 0xe7, 0xe7, 0x08, 0x89, 0x71, 0x31, 0x65, 0xa6, 0x48, 0x30, 0x2a, - 0x30, 0x6a, 0xf0, 0x3a, 0xb1, 0x3d, 0xba, 0x27, 0xcf, 0xe4, 0xe9, 0x12, 0xc4, 0x94, 0x99, 0x22, - 0x24, 0xc2, 0xc5, 0x9a, 0x92, 0x9a, 0x97, 0x9f, 0x2b, 0xc1, 0xa4, 0xc0, 0xa8, 0xc1, 0x19, 0x04, - 0xe1, 0x08, 0x19, 0x71, 0xb1, 0x27, 0xa6, 0xa4, 0x14, 0xa5, 0x16, 0x17, 0x4b, 0x30, 0x83, 0xc4, - 0x9d, 0x24, 0x2e, 0x6d, 0xd1, 0x15, 0x81, 0x1a, 0xec, 0x08, 0x91, 0x09, 0x2e, 0x29, 0xca, 0xcc, - 0x4b, 0x0f, 0x82, 0x29, 0x74, 0xf2, 0x3c, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07, - 0x8f, 0xe4, 0x18, 0x27, 0x3c, 0x96, 0x63, 0xb8, 0xf0, 0x58, 0x8e, 0xe1, 0xc6, 0x63, 0x39, 0x86, - 0x28, 0xfd, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0x7d, 0x98, 0x97, 0x74, - 0x73, 0x12, 0x93, 0x8a, 0xe1, 0x3c, 0xfd, 0x0a, 0x68, 0x10, 0x94, 0x54, 0x16, 0xa4, 0x16, 0x27, - 0xb1, 0x81, 0xdd, 0x6e, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0xee, 0x3f, 0xe7, 0x79, 0x21, 0x01, + // 338 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x91, 0x41, 0x4a, 0xc3, 0x40, + 0x14, 0x86, 0x93, 0xd8, 0x56, 0x1c, 0x10, 0x35, 0x54, 0x89, 0x15, 0xa6, 0xc5, 0x55, 0x41, 0x9a, + 0xa1, 0x7a, 0x02, 0x6b, 0x37, 0x01, 0x17, 0x92, 0xee, 0x44, 0x28, 0xd3, 0xcc, 0x90, 0x86, 0x26, + 0x79, 0x25, 0x33, 0x56, 0x73, 0x06, 0x37, 0x1e, 0xa6, 0x87, 0xe8, 0xb2, 0x74, 0x25, 0x2e, 0x8a, + 0xa4, 0x17, 0x91, 0xce, 0xa4, 0x45, 0x70, 0x37, 0xff, 0x7c, 0xef, 0xff, 0x16, 0xef, 0x21, 0x9c, + 0x44, 0xf1, 0x24, 0x7f, 0xa3, 0x39, 0x99, 0x02, 0xc4, 0x82, 0xcc, 0xba, 0x24, 0x01, 0xc6, 0x63, + 0xe1, 0x4e, 0x33, 0x90, 0x60, 0x9f, 0xed, 0xb8, 0xab, 0xb8, 0x3b, 0xeb, 0x36, 0xea, 0x21, 0x84, + 0xa0, 0x28, 0xd9, 0xbe, 0xf4, 0x60, 0xe3, 0x32, 0x00, 0x91, 0x80, 0x18, 0x6a, 0xa0, 0x83, 0x46, + 0xd7, 0x1f, 0x16, 0xaa, 0x3c, 0x01, 0xc4, 0xf6, 0x05, 0xb2, 0x22, 0xe6, 0x98, 0x2d, 0xb3, 0x7d, + 0xdc, 0xab, 0x15, 0xeb, 0xa6, 0xe5, 0xf5, 0x7d, 0x2b, 0x62, 0x76, 0x1d, 0x55, 0x19, 0x4f, 0x21, + 0x71, 0xac, 0x96, 0xd9, 0x3e, 0xf2, 0x75, 0xb0, 0x6f, 0xd1, 0x21, 0x65, 0x2c, 0xe3, 0x42, 0x38, + 0x07, 0xdb, 0xff, 0x9e, 0xb3, 0x9a, 0x77, 0xea, 0xa5, 0xf9, 0x5e, 0x93, 0x81, 0xcc, 0xa2, 0x34, + 0xf4, 0x77, 0x83, 0xf6, 0x03, 0xaa, 0x49, 0x98, 0xf0, 0x54, 0x38, 0x15, 0x55, 0xb9, 0x59, 0xac, + 0x9b, 0xc6, 0xf7, 0xba, 0x79, 0xae, 0x6b, 0x82, 0x4d, 0xdc, 0x08, 0x48, 0x42, 0xe5, 0xd8, 0xf5, + 0x52, 0xb9, 0x9a, 0x77, 0x50, 0xe9, 0xf3, 0x52, 0xe9, 0x97, 0x55, 0xfb, 0x05, 0x9d, 0x32, 0x1e, + 0xf3, 0x90, 0x4a, 0xc8, 0x86, 0x62, 0x4c, 0x33, 0x2e, 0x9c, 0xaa, 0xd2, 0x75, 0x4b, 0xdd, 0xd5, + 0x7f, 0xdd, 0x23, 0x0f, 0x69, 0x90, 0xf7, 0x79, 0xf0, 0x47, 0xda, 0xe7, 0x81, 0x7f, 0xb2, 0x57, + 0x0d, 0x94, 0xa9, 0xe7, 0x2d, 0x0a, 0x6c, 0x2e, 0x0b, 0x6c, 0xfe, 0x14, 0xd8, 0xfc, 0xdc, 0x60, + 0x63, 0xb9, 0xc1, 0xc6, 0xd7, 0x06, 0x1b, 0xcf, 0x24, 0x8c, 0xe4, 0xf8, 0x75, 0xe4, 0x06, 0x90, + 0x90, 0xdd, 0xda, 0x3b, 0x31, 0x1d, 0x89, 0x7d, 0x22, 0xef, 0xe5, 0x99, 0x64, 0x3e, 0xe5, 0x62, + 0x54, 0x53, 0xfb, 0xbd, 0xfb, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xf0, 0xfd, 0xb0, 0x49, 0xc5, 0x01, 0x00, 0x00, } @@ -135,6 +146,26 @@ func (m *Pool) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + { + size := m.DelegatorShares.Size() + i -= size + if _, err := m.DelegatorShares.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintModels(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + { + size := m.Tokens.Size() + i -= size + if _, err := m.Tokens.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintModels(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 if len(m.Address) > 0 { i -= len(m.Address) copy(dAtA[i:], m.Address) @@ -185,6 +216,10 @@ func (m *Pool) Size() (n int) { if l > 0 { n += 1 + l + sovModels(uint64(l)) } + l = m.Tokens.Size() + n += 1 + l + sovModels(uint64(l)) + l = m.DelegatorShares.Size() + n += 1 + l + sovModels(uint64(l)) return n } @@ -306,6 +341,74 @@ func (m *Pool) Unmarshal(dAtA []byte) error { } m.Address = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Tokens", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthModels + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthModels + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Tokens.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorShares", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthModels + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthModels + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.DelegatorShares.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipModels(dAtA[iNdEx:]) diff --git a/x/restaking/keeper/alias_functions.go b/x/restaking/keeper/alias_functions.go new file mode 100644 index 000000000..8b25557de --- /dev/null +++ b/x/restaking/keeper/alias_functions.go @@ -0,0 +1,67 @@ +package keeper + +import ( + "fmt" + + storetypes "cosmossdk.io/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/milkyway-labs/milkyway/x/restaking/types" +) + +// GetAllPoolDelegations returns all the pool delegations +func (k *Keeper) GetAllPoolDelegations(ctx sdk.Context) []types.PoolDelegation { + store := ctx.KVStore(k.storeKey) + iterator := store.Iterator(types.PoolDelegationPrefix, storetypes.PrefixEndBytes(types.PoolDelegationPrefix)) + defer iterator.Close() + + var delegations []types.PoolDelegation + for ; iterator.Valid(); iterator.Next() { + delegation := types.MustUnmarshalPoolDelegation(k.cdc, iterator.Value()) + delegations = append(delegations, delegation) + } + + return delegations +} + +// GetAllDelegatorPoolDelegations returns all the pool delegations of a given delegator +func (k *Keeper) GetAllDelegatorPoolDelegations(ctx sdk.Context, delegator string) []types.PoolDelegation { + store := ctx.KVStore(k.storeKey) + delegatorPrefixKey := types.UserPoolDelegationsStorePrefix(delegator) + + iterator := store.Iterator(delegatorPrefixKey, storetypes.PrefixEndBytes(delegatorPrefixKey)) // Smallest to largest + defer iterator.Close() + + var delegations []types.PoolDelegation + for ; iterator.Valid(); iterator.Next() { + delegation := types.MustUnmarshalPoolDelegation(k.cdc, iterator.Value()) + delegations = append(delegations, delegation) + } + + return delegations +} + +// GetPoolDelegations returns all the delegations to a given pool +func (k *Keeper) GetPoolDelegations(ctx sdk.Context, poolID uint32) ([]types.PoolDelegation, error) { + store := ctx.KVStore(k.storeKey) + prefix := types.DelegationsByPoolIDStorePrefix(poolID) + iterator := store.Iterator(prefix, storetypes.PrefixEndBytes(prefix)) + defer iterator.Close() + + var delegations []types.PoolDelegation + for ; iterator.Valid(); iterator.Next() { + _, delegatorAddress, err := types.ParseDelegationsByPoolIDKey(iterator.Key()) + if err != nil { + return nil, err + } + + delegation, found := k.GetPoolDelegation(ctx, poolID, delegatorAddress) + if !found { + return nil, fmt.Errorf("delegation not found for pool %d and delegator %s", poolID, delegatorAddress) + } + + delegations = append(delegations, delegation) + } + + return delegations, nil +} diff --git a/x/restaking/keeper/alias_functions_test.go b/x/restaking/keeper/alias_functions_test.go new file mode 100644 index 000000000..294999f04 --- /dev/null +++ b/x/restaking/keeper/alias_functions_test.go @@ -0,0 +1,282 @@ +package keeper_test + +import ( + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/milkyway-labs/milkyway/x/restaking/types" +) + +func (suite *KeeperTestSuite) TestKeeper_GetAllPoolDelegations() { + testCases := []struct { + name string + setup func() + store func(ctx sdk.Context) + shouldErr bool + expDelegations []types.PoolDelegation + check func(ctx sdk.Context) + }{ + { + name: "delegations are returned properly", + store: func(ctx sdk.Context) { + suite.k.SavePoolDelegation(ctx, types.NewPoolDelegation( + 1, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdkmath.LegacyNewDec(100), + )) + suite.k.SavePoolDelegation(ctx, types.NewPoolDelegation( + 2, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdkmath.LegacyNewDec(50), + )) + suite.k.SavePoolDelegation(ctx, types.NewPoolDelegation( + 1, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdkmath.LegacyNewDec(100), + )) + }, + expDelegations: []types.PoolDelegation{ + types.NewPoolDelegation( + 1, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdkmath.LegacyNewDec(100), + ), + types.NewPoolDelegation( + 2, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdkmath.LegacyNewDec(50), + ), + types.NewPoolDelegation( + 1, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdkmath.LegacyNewDec(100), + ), + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.setup != nil { + tc.setup() + } + if tc.store != nil { + tc.store(ctx) + } + + delegations := suite.k.GetAllPoolDelegations(ctx) + suite.Require().Equal(tc.expDelegations, delegations) + + if tc.check != nil { + tc.check(ctx) + } + }) + } +} + +func (suite *KeeperTestSuite) TestKeeper_GetAllDelegatorPoolDelegations() { + testCases := []struct { + name string + setup func() + store func(ctx sdk.Context) + delegator string + expDelegations []types.PoolDelegation + check func(ctx sdk.Context) + }{ + { + name: "user without delegations returns empty list", + delegator: "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + expDelegations: nil, + }, + { + name: "user with single delegation returns it properly", + store: func(ctx sdk.Context) { + suite.k.SavePoolDelegation(ctx, types.NewPoolDelegation( + 1, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdkmath.LegacyNewDec(100), + )) + suite.k.SavePoolDelegation(ctx, types.NewPoolDelegation( + 2, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdkmath.LegacyNewDec(50), + )) + suite.k.SavePoolDelegation(ctx, types.NewPoolDelegation( + 1, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdkmath.LegacyNewDec(100), + )) + }, + delegator: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + expDelegations: []types.PoolDelegation{ + types.NewPoolDelegation( + 1, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdkmath.LegacyNewDec(100), + ), + }, + }, + { + name: "user with multiple delegations returns them properly", + store: func(ctx sdk.Context) { + suite.k.SavePoolDelegation(ctx, types.NewPoolDelegation( + 1, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdkmath.LegacyNewDec(100), + )) + suite.k.SavePoolDelegation(ctx, types.NewPoolDelegation( + 2, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdkmath.LegacyNewDec(50), + )) + suite.k.SavePoolDelegation(ctx, types.NewPoolDelegation( + 1, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdkmath.LegacyNewDec(100), + )) + }, + delegator: "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + expDelegations: []types.PoolDelegation{ + types.NewPoolDelegation( + 1, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdkmath.LegacyNewDec(100), + ), + types.NewPoolDelegation( + 2, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdkmath.LegacyNewDec(50), + ), + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.setup != nil { + tc.setup() + } + if tc.store != nil { + tc.store(ctx) + } + + delegations := suite.k.GetAllDelegatorPoolDelegations(ctx, tc.delegator) + suite.Require().Equal(tc.expDelegations, delegations) + + if tc.check != nil { + tc.check(ctx) + } + }) + } +} + +func (suite *KeeperTestSuite) TestKeeper_GetPoolDelegations() { + testCases := []struct { + name string + setup func() + store func(ctx sdk.Context) + poolID uint32 + shouldErr bool + expDelegations []types.PoolDelegation + check func(ctx sdk.Context) + }{ + { + name: "pool without delegations returns empty list", + poolID: 1, + shouldErr: false, + expDelegations: nil, + }, + { + name: "pool with single delegation returns it properly", + store: func(ctx sdk.Context) { + suite.k.SavePoolDelegation(ctx, types.NewPoolDelegation( + 1, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdkmath.LegacyNewDec(100), + )) + suite.k.SavePoolDelegation(ctx, types.NewPoolDelegation( + 2, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdkmath.LegacyNewDec(50), + )) + suite.k.SavePoolDelegation(ctx, types.NewPoolDelegation( + 1, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdkmath.LegacyNewDec(100), + )) + }, + poolID: 2, + shouldErr: false, + expDelegations: []types.PoolDelegation{ + types.NewPoolDelegation( + 2, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdkmath.LegacyNewDec(50), + ), + }, + }, + { + name: "pool with multiple delegations returns them properly", + store: func(ctx sdk.Context) { + suite.k.SavePoolDelegation(ctx, types.NewPoolDelegation( + 1, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdkmath.LegacyNewDec(100), + )) + suite.k.SavePoolDelegation(ctx, types.NewPoolDelegation( + 2, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdkmath.LegacyNewDec(50), + )) + suite.k.SavePoolDelegation(ctx, types.NewPoolDelegation( + 1, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdkmath.LegacyNewDec(100), + )) + }, + poolID: 1, + shouldErr: false, + expDelegations: []types.PoolDelegation{ + types.NewPoolDelegation( + 1, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdkmath.LegacyNewDec(100), + ), + types.NewPoolDelegation( + 1, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdkmath.LegacyNewDec(100), + ), + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.setup != nil { + tc.setup() + } + if tc.store != nil { + tc.store(ctx) + } + + delegations, err := suite.k.GetPoolDelegations(ctx, tc.poolID) + if tc.shouldErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + suite.Require().Equal(tc.expDelegations, delegations) + } + + if tc.check != nil { + tc.check(ctx) + } + }) + } +} diff --git a/x/restaking/keeper/common_test.go b/x/restaking/keeper/common_test.go new file mode 100644 index 000000000..5cef6e5ae --- /dev/null +++ b/x/restaking/keeper/common_test.go @@ -0,0 +1,212 @@ +package keeper_test + +import ( + "testing" + + "cosmossdk.io/log" + "cosmossdk.io/store" + "cosmossdk.io/store/metrics" + "github.com/cosmos/cosmos-sdk/runtime" + authcodec "github.com/cosmos/cosmos-sdk/x/auth/codec" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + + "github.com/milkyway-labs/milkyway/app" + appkeepers "github.com/milkyway-labs/milkyway/app/keepers" + bankkeeper "github.com/milkyway-labs/milkyway/x/bank/keeper" + operatorskeeper "github.com/milkyway-labs/milkyway/x/operators/keeper" + operatorstypes "github.com/milkyway-labs/milkyway/x/operators/types" + poolskeeper "github.com/milkyway-labs/milkyway/x/pools/keeper" + poolstypes "github.com/milkyway-labs/milkyway/x/pools/types" + "github.com/milkyway-labs/milkyway/x/restaking/keeper" + "github.com/milkyway-labs/milkyway/x/restaking/types" + serviceskeeper "github.com/milkyway-labs/milkyway/x/services/keeper" + servicestypes "github.com/milkyway-labs/milkyway/x/services/types" + + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + + storetypes "cosmossdk.io/store/types" + db "github.com/cosmos/cosmos-db" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/suite" +) + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} + +type KeeperTestSuite struct { + suite.Suite + + cdc codec.Codec + legacyAminoCdc *codec.LegacyAmino + ctx sdk.Context + + storeKey storetypes.StoreKey + + ak authkeeper.AccountKeeper + bk bankkeeper.Keeper + pk *poolskeeper.Keeper + ok *operatorskeeper.Keeper + sk *serviceskeeper.Keeper + k *keeper.Keeper +} + +func (suite *KeeperTestSuite) SetupTest() { + // Define store keys + keys := storetypes.NewKVStoreKeys( + types.StoreKey, + authtypes.StoreKey, banktypes.StoreKey, poolstypes.StoreKey, operatorstypes.StoreKey, servicestypes.StoreKey, + ) + suite.storeKey = keys[types.StoreKey] + + // Create logger + logger := log.NewNopLogger() + + // Create an in-memory db + memDB := db.NewMemDB() + ms := store.NewCommitMultiStore(memDB, logger, metrics.NewNoOpMetrics()) + for _, key := range keys { + ms.MountStoreWithDB(key, storetypes.StoreTypeIAVL, memDB) + } + + if err := ms.LoadLatestVersion(); err != nil { + panic(err) + } + + suite.ctx = sdk.NewContext(ms, tmproto.Header{ChainID: "test-chain"}, false, log.NewNopLogger()) + suite.cdc, suite.legacyAminoCdc = app.MakeCodecs() + + // Authority address + authorityAddr := authtypes.NewModuleAddress(govtypes.ModuleName).String() + + // Build keepers + + suite.ak = authkeeper.NewAccountKeeper( + suite.cdc, + runtime.NewKVStoreService(keys[authtypes.StoreKey]), + authtypes.ProtoBaseAccount, + app.GetMaccPerms(), + authcodec.NewBech32Codec(sdk.GetConfig().GetBech32AccountAddrPrefix()), + sdk.GetConfig().GetBech32AccountAddrPrefix(), + authorityAddr, + ) + suite.bk = bankkeeper.NewKeeper( + suite.cdc, + runtime.NewKVStoreService(keys[banktypes.StoreKey]), + suite.ak, + nil, + authorityAddr, + logger, + ) + communityPoolKeeper := appkeepers.NewCommunityPoolKeeper( + suite.bk, + authtypes.FeeCollectorName, + ) + suite.pk = poolskeeper.NewKeeper( + suite.cdc, + keys[poolstypes.StoreKey], + suite.ak, + ) + suite.ok = operatorskeeper.NewKeeper( + suite.cdc, + keys[operatorstypes.StoreKey], + suite.ak, + communityPoolKeeper, + authorityAddr, + ) + suite.sk = serviceskeeper.NewKeeper( + suite.cdc, + keys[servicestypes.StoreKey], + suite.ak, + communityPoolKeeper, + authorityAddr, + ) + suite.k = keeper.NewKeeper( + suite.cdc, + suite.storeKey, + suite.ak, + suite.bk, + suite.pk, + suite.ok, + suite.sk, + authorityAddr, + ).SetHooks(newMockHooks()) +} + +// -------------------------------------------------------------------------------------------------------------------- + +// fundAccount adds the given amount of coins to the account with the given address +func (suite *KeeperTestSuite) fundAccount(ctx sdk.Context, address string, amount sdk.Coins) { + // Mint the coins + moduleAcc := suite.ak.GetModuleAccount(ctx, authtypes.Minter) + + err := suite.bk.MintCoins(ctx, moduleAcc.GetName(), amount) + suite.Require().NoError(err) + + // Get the amount to the user + userAddress, err := sdk.AccAddressFromBech32(address) + suite.Require().NoError(err) + err = suite.bk.SendCoinsFromModuleToAccount(ctx, moduleAcc.GetName(), userAddress, amount) + suite.Require().NoError(err) +} + +// -------------------------------------------------------------------------------------------------------------------- + +var _ types.RestakingHooks = &mockHooks{} + +type mockHooks struct { + CalledMap map[string]bool +} + +func newMockHooks() *mockHooks { + return &mockHooks{CalledMap: make(map[string]bool)} +} + +func (m mockHooks) BeforePoolDelegationCreated(sdk.Context, uint32, string) error { + m.CalledMap["BeforePoolDelegationCreated"] = true + return nil +} + +func (m mockHooks) BeforePoolDelegationSharesModified(sdk.Context, uint32, string) error { + m.CalledMap["BeforePoolDelegationSharesModified"] = true + return nil +} + +func (m mockHooks) AfterPoolDelegationModified(sdk.Context, uint32, string) error { + m.CalledMap["AfterPoolDelegationModified"] = true + return nil +} + +func (m mockHooks) BeforeOperatorDelegationCreated(sdk.Context, uint32, string) error { + m.CalledMap["BeforeOperatorDelegationCreated"] = true + return nil +} + +func (m mockHooks) BeforeOperatorDelegationSharesModified(sdk.Context, uint32, string) error { + m.CalledMap["BeforeOperatorDelegationSharesModified"] = true + return nil +} + +func (m mockHooks) AfterOperatorDelegationModified(sdk.Context, uint32, string) error { + m.CalledMap["AfterOperatorDelegationModified"] = true + return nil +} + +func (m mockHooks) BeforeServiceDelegationCreated(sdk.Context, uint32, string) error { + m.CalledMap["BeforeServiceDelegationCreated"] = true + return nil +} + +func (m mockHooks) BeforeServiceDelegationSharesModified(sdk.Context, uint32, string) error { + m.CalledMap["BeforeServiceDelegationSharesModified"] = true + return nil +} + +func (m mockHooks) AfterServiceDelegationModified(sdk.Context, uint32, string) error { + m.CalledMap["AfterServiceDelegationModified"] = true + return nil +} diff --git a/x/restaking/keeper/hooks.go b/x/restaking/keeper/hooks.go new file mode 100644 index 000000000..960fda0e8 --- /dev/null +++ b/x/restaking/keeper/hooks.go @@ -0,0 +1,85 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/milkyway-labs/milkyway/x/restaking/types" +) + +var _ types.RestakingHooks = &Keeper{} + +// BeforePoolDelegationCreated implements types.RestakingHooks +func (k *Keeper) BeforePoolDelegationCreated(ctx sdk.Context, poolID uint32, delegator string) error { + if k.hooks != nil { + return k.hooks.BeforePoolDelegationCreated(ctx, poolID, delegator) + } + return nil +} + +// BeforePoolDelegationSharesModified implements types.RestakingHooks +func (k *Keeper) BeforePoolDelegationSharesModified(ctx sdk.Context, poolID uint32, delegator string) error { + if k.hooks != nil { + return k.hooks.BeforePoolDelegationSharesModified(ctx, poolID, delegator) + } + return nil +} + +// AfterPoolDelegationModified implements types.RestakingHooks +func (k *Keeper) AfterPoolDelegationModified(ctx sdk.Context, poolID uint32, delegator string) error { + if k.hooks != nil { + return k.hooks.AfterPoolDelegationModified(ctx, poolID, delegator) + } + return nil +} + +// -------------------------------------------------------------------------------------------------------------------- + +// BeforeOperatorDelegationCreated implements types.RestakingHooks +func (k *Keeper) BeforeOperatorDelegationCreated(ctx sdk.Context, operatorID uint32, delegator string) error { + if k.hooks != nil { + return k.hooks.BeforeOperatorDelegationCreated(ctx, operatorID, delegator) + } + return nil +} + +// BeforeOperatorDelegationSharesModified implements types.RestakingHooks +func (k *Keeper) BeforeOperatorDelegationSharesModified(ctx sdk.Context, operatorID uint32, delegator string) error { + if k.hooks != nil { + return k.hooks.BeforeOperatorDelegationSharesModified(ctx, operatorID, delegator) + } + return nil +} + +// AfterOperatorDelegationModified implements types.RestakingHooks +func (k *Keeper) AfterOperatorDelegationModified(ctx sdk.Context, operatorID uint32, delegator string) error { + if k.hooks != nil { + return k.hooks.AfterOperatorDelegationModified(ctx, operatorID, delegator) + } + return nil +} + +// -------------------------------------------------------------------------------------------------------------------- + +// BeforeServiceDelegationCreated implements types.RestakingHooks +func (k *Keeper) BeforeServiceDelegationCreated(ctx sdk.Context, serviceID uint32, delegator string) error { + if k.hooks != nil { + return k.hooks.BeforeServiceDelegationCreated(ctx, serviceID, delegator) + } + return nil +} + +// BeforeServiceDelegationSharesModified implements types.RestakingHooks +func (k *Keeper) BeforeServiceDelegationSharesModified(ctx sdk.Context, serviceID uint32, delegator string) error { + if k.hooks != nil { + return k.hooks.BeforeServiceDelegationSharesModified(ctx, serviceID, delegator) + } + return nil +} + +// AfterServiceDelegationModified implements types.RestakingHooks +func (k *Keeper) AfterServiceDelegationModified(ctx sdk.Context, serviceID uint32, delegator string) error { + if k.hooks != nil { + return k.hooks.AfterServiceDelegationModified(ctx, serviceID, delegator) + } + return nil +} diff --git a/x/restaking/keeper/keeper.go b/x/restaking/keeper/keeper.go new file mode 100644 index 000000000..aa923804b --- /dev/null +++ b/x/restaking/keeper/keeper.go @@ -0,0 +1,70 @@ +package keeper + +import ( + "cosmossdk.io/log" + storetypes "cosmossdk.io/store/types" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/milkyway-labs/milkyway/x/restaking/types" +) + +type Keeper struct { + storeKey storetypes.StoreKey + cdc codec.Codec + + authority string + + accountKeeper types.AccountKeeper + bankKeeper types.BankKeeper + poolsKeeper types.PoolsKeeper + operatorsKeeper types.OperatorsKeeper + servicesKeeper types.ServicesKeeper + + hooks types.RestakingHooks +} + +func NewKeeper( + cdc codec.Codec, + storeKey storetypes.StoreKey, + accountKeeper types.AccountKeeper, + bankKeeper types.BankKeeper, + poolsKeeper types.PoolsKeeper, + operatorsKeeper types.OperatorsKeeper, + servicesKeeper types.ServicesKeeper, + authority string, +) *Keeper { + + // Ensure that authority is a valid AccAddress + if _, err := accountKeeper.AddressCodec().StringToBytes(authority); err != nil { + panic("authority is not a valid account address") + } + + return &Keeper{ + storeKey: storeKey, + cdc: cdc, + + accountKeeper: accountKeeper, + bankKeeper: bankKeeper, + poolsKeeper: poolsKeeper, + operatorsKeeper: operatorsKeeper, + servicesKeeper: servicesKeeper, + + authority: authority, + } +} + +// Logger returns a module-specific logger. +func (k *Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", "x/"+types.ModuleName) +} + +// SetHooks allows to set the reactions hooks +func (k *Keeper) SetHooks(rs types.RestakingHooks) *Keeper { + if k.hooks != nil { + panic("cannot set services hooks twice") + } + + k.hooks = rs + return k +} diff --git a/x/restaking/keeper/msg_server.go b/x/restaking/keeper/msg_server.go new file mode 100644 index 000000000..790061188 --- /dev/null +++ b/x/restaking/keeper/msg_server.go @@ -0,0 +1,168 @@ +package keeper + +import ( + "context" + "fmt" + + "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/telemetry" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/hashicorp/go-metrics" + + "github.com/milkyway-labs/milkyway/x/restaking/types" +) + +var ( + _ types.MsgServer = msgServer{} +) + +type msgServer struct { + *Keeper +} + +func NewMsgServer(keeper *Keeper) types.MsgServer { + return &msgServer{Keeper: keeper} +} + +// DelegatePool defines the rpc method for Msg/DelegatePool +func (k msgServer) DelegatePool(goCtx context.Context, msg *types.MsgDelegatePool) (*types.MsgDelegatePoolResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + if !msg.Amount.IsValid() || !msg.Amount.Amount.IsPositive() { + return nil, errors.Wrap( + sdkerrors.ErrInvalidRequest, + "invalid delegation amount", + ) + } + + newShares, err := k.Keeper.DelegateToPool(ctx, msg.Amount, msg.Delegator) + if err != nil { + return nil, err + } + + if msg.Amount.Amount.IsInt64() { + defer func() { + telemetry.IncrCounter(1, types.ModuleName, "pool restake") + telemetry.SetGaugeWithLabels( + []string{"tx", "msg", sdk.MsgTypeURL(msg)}, + float32(msg.Amount.Amount.Int64()), + []metrics.Label{telemetry.NewLabel("denom", msg.Amount.Denom)}, + ) + }() + } + + sdkCtx := sdk.UnwrapSDKContext(ctx) + sdkCtx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeDelegatePool, + sdk.NewAttribute(types.AttributeKeyDelegator, msg.Delegator), + sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()), + sdk.NewAttribute(types.AttributeKeyNewShares, newShares.String()), + ), + }) + + return &types.MsgDelegatePoolResponse{}, nil +} + +// DelegateOperator defines the rpc method for Msg/DelegateOperator +func (k msgServer) DelegateOperator(goCtx context.Context, msg *types.MsgDelegateOperator) (*types.MsgDelegateOperatorResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + if !msg.Amount.IsValid() || !msg.Amount.IsAllPositive() { + return nil, errors.Wrap( + sdkerrors.ErrInvalidRequest, + "invalid delegation amount", + ) + } + + newShares, err := k.Keeper.DelegateToOperator(ctx, msg.OperatorID, msg.Amount, msg.Delegator) + if err != nil { + return nil, err + } + + for _, token := range msg.Amount { + if token.Amount.IsInt64() { + defer func() { + telemetry.IncrCounter(1, types.ModuleName, "operator restake") + telemetry.SetGaugeWithLabels( + []string{"tx", "msg", sdk.MsgTypeURL(msg)}, + float32(token.Amount.Int64()), + []metrics.Label{telemetry.NewLabel("denom", token.Denom)}, + ) + }() + } + } + + sdkCtx := sdk.UnwrapSDKContext(ctx) + sdkCtx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeDelegateOperator, + sdk.NewAttribute(types.AttributeKeyDelegator, msg.Delegator), + sdk.NewAttribute(types.AttributeKeyOperatorID, fmt.Sprintf("%d", msg.OperatorID)), + sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()), + sdk.NewAttribute(types.AttributeKeyNewShares, newShares.String()), + ), + }) + + return &types.MsgDelegateOperatorResponse{}, nil +} + +// DelegateService defines the rpc method for Msg/DelegateService +func (k msgServer) DelegateService(goCtx context.Context, msg *types.MsgDelegateService) (*types.MsgDelegateServiceResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + if !msg.Amount.IsValid() || !msg.Amount.IsAllPositive() { + return nil, errors.Wrap( + sdkerrors.ErrInvalidRequest, + "invalid delegation amount", + ) + } + + newShares, err := k.Keeper.DelegateToService(ctx, msg.ServiceID, msg.Amount, msg.Delegator) + if err != nil { + return nil, err + } + + for _, token := range msg.Amount { + if token.Amount.IsInt64() { + defer func() { + telemetry.IncrCounter(1, types.ModuleName, "service restake") + telemetry.SetGaugeWithLabels( + []string{"tx", "msg", sdk.MsgTypeURL(msg)}, + float32(token.Amount.Int64()), + []metrics.Label{telemetry.NewLabel("denom", token.Denom)}, + ) + }() + } + } + + sdkCtx := sdk.UnwrapSDKContext(ctx) + sdkCtx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeDelegateService, + sdk.NewAttribute(types.AttributeKeyDelegator, msg.Delegator), + sdk.NewAttribute(types.AttributeKeyServiceID, fmt.Sprintf("%d", msg.ServiceID)), + sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()), + sdk.NewAttribute(types.AttributeKeyNewShares, newShares.String()), + ), + }) + + return &types.MsgDelegateServiceResponse{}, nil +} + +// UpdateParams defines the rpc method for Msg/UpdateParams +func (k msgServer) UpdateParams(goCtx context.Context, msg *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error) { + // Check the authority + authority := k.authority + if authority != msg.Authority { + return nil, errors.Wrapf(govtypes.ErrInvalidSigner, "invalid authority; expected %s, got %s", authority, msg.Authority) + } + + // Update the params + ctx := sdk.UnwrapSDKContext(goCtx) + k.SetParams(ctx, msg.Params) + + return &types.MsgUpdateParamsResponse{}, nil +} diff --git a/x/restaking/keeper/msg_server_test.go b/x/restaking/keeper/msg_server_test.go new file mode 100644 index 000000000..059d971b7 --- /dev/null +++ b/x/restaking/keeper/msg_server_test.go @@ -0,0 +1,412 @@ +package keeper_test + +import ( + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + + operatorstypes "github.com/milkyway-labs/milkyway/x/operators/types" + poolstypes "github.com/milkyway-labs/milkyway/x/pools/types" + "github.com/milkyway-labs/milkyway/x/restaking/keeper" + "github.com/milkyway-labs/milkyway/x/restaking/types" + servicestypes "github.com/milkyway-labs/milkyway/x/services/types" +) + +func (suite *KeeperTestSuite) TestMsgServer_DelegatePool() { + testCases := []struct { + name string + setup func() + store func(ctx sdk.Context) + setupCtx func(ctx sdk.Context) sdk.Context + msg *types.MsgDelegatePool + shouldErr bool + expEvents sdk.Events + check func(ctx sdk.Context) + }{ + { + name: "invalid amount returns error", + store: func(ctx sdk.Context) { + // Create the pool + err := suite.pk.SavePool(ctx, poolstypes.Pool{ + ID: 1, + Denom: "umilk", + Address: poolstypes.GetPoolAddress(1).String(), + Tokens: sdkmath.NewInt(20), + DelegatorShares: sdkmath.LegacyNewDec(100), + }) + suite.Require().NoError(err) + + // Send some funds to the user + suite.fundAccount( + ctx, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100))), + ) + }, + msg: &types.MsgDelegatePool{ + Delegator: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + Amount: sdk.NewCoin("umilk", sdkmath.NewInt(0)), + }, + shouldErr: true, + }, + { + name: "valid amount is delegated properly", + store: func(ctx sdk.Context) { + // Create the pool + err := suite.pk.SavePool(ctx, poolstypes.Pool{ + ID: 1, + Denom: "umilk", + Address: poolstypes.GetPoolAddress(1).String(), + Tokens: sdkmath.NewInt(20), + DelegatorShares: sdkmath.LegacyNewDec(100), + }) + suite.Require().NoError(err) + + // Send some funds to the user + suite.fundAccount( + ctx, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100))), + ) + }, + msg: &types.MsgDelegatePool{ + Delegator: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + Amount: sdk.NewCoin("umilk", sdkmath.NewInt(100)), + }, + shouldErr: false, + expEvents: sdk.Events{ + sdk.NewEvent( + types.EventTypeDelegatePool, + sdk.NewAttribute(types.AttributeKeyDelegator, "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4"), + sdk.NewAttribute(sdk.AttributeKeyAmount, "100umilk"), + sdk.NewAttribute(types.AttributeKeyNewShares, "500.000000000000000000pool/1/umilk"), + ), + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.setup != nil { + tc.setup() + } + if tc.setupCtx != nil { + ctx = tc.setupCtx(ctx) + } + if tc.store != nil { + tc.store(ctx) + } + + msgServer := keeper.NewMsgServer(suite.k) + _, err := msgServer.DelegatePool(ctx, tc.msg) + if tc.shouldErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + for _, event := range tc.expEvents { + suite.Require().Contains(ctx.EventManager().Events(), event) + } + + if tc.check != nil { + tc.check(ctx) + } + } + }) + } +} + +func (suite *KeeperTestSuite) TestMsgServer_DelegateOperator() { + testCases := []struct { + name string + setup func() + store func(ctx sdk.Context) + setupCtx func(ctx sdk.Context) sdk.Context + msg *types.MsgDelegateOperator + shouldErr bool + expEvents sdk.Events + check func(ctx sdk.Context) + }{ + { + name: "invalid amount returns error", + store: func(ctx sdk.Context) { + // Create the operator + suite.ok.SaveOperator(ctx, operatorstypes.Operator{ + ID: 1, + Status: operatorstypes.OPERATOR_STATUS_ACTIVE, + Address: operatorstypes.GetOperatorAddress(1).String(), + Tokens: sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(20)), + ), + DelegatorShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("operator/1/umilk", sdkmath.LegacyNewDec(100)), + ), + }) + + // Send some funds to the user + suite.fundAccount( + ctx, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100))), + ) + }, + msg: &types.MsgDelegateOperator{ + OperatorID: 1, + Delegator: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + Amount: sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(0))), + }, + shouldErr: true, + }, + { + name: "valid amount is delegated properly", + store: func(ctx sdk.Context) { + // Create the operator + suite.ok.SaveOperator(ctx, operatorstypes.Operator{ + ID: 1, + Status: operatorstypes.OPERATOR_STATUS_ACTIVE, + Address: operatorstypes.GetOperatorAddress(1).String(), + Tokens: sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(20)), + ), + DelegatorShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("operator/1/umilk", sdkmath.LegacyNewDec(100)), + ), + }) + + // Send some funds to the user + suite.fundAccount( + ctx, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100))), + ) + }, + msg: &types.MsgDelegateOperator{ + OperatorID: 1, + Delegator: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + Amount: sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100))), + }, + shouldErr: false, + expEvents: sdk.Events{ + sdk.NewEvent( + types.EventTypeDelegateOperator, + sdk.NewAttribute(types.AttributeKeyDelegator, "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4"), + sdk.NewAttribute(types.AttributeKeyOperatorID, "1"), + sdk.NewAttribute(sdk.AttributeKeyAmount, "100umilk"), + sdk.NewAttribute(types.AttributeKeyNewShares, "500.000000000000000000operator/1/umilk"), + ), + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.setup != nil { + tc.setup() + } + if tc.setupCtx != nil { + ctx = tc.setupCtx(ctx) + } + if tc.store != nil { + tc.store(ctx) + } + + msgServer := keeper.NewMsgServer(suite.k) + _, err := msgServer.DelegateOperator(ctx, tc.msg) + if tc.shouldErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + for _, event := range tc.expEvents { + suite.Require().Contains(ctx.EventManager().Events(), event) + } + + if tc.check != nil { + tc.check(ctx) + } + } + }) + } +} + +func (suite *KeeperTestSuite) TestMsgServer_DelegateService() { + testCases := []struct { + name string + setup func() + store func(ctx sdk.Context) + setupCtx func(ctx sdk.Context) sdk.Context + msg *types.MsgDelegateService + shouldErr bool + expEvents sdk.Events + check func(ctx sdk.Context) + }{ + { + name: "invalid amount returns error", + store: func(ctx sdk.Context) { + // Create the service + suite.sk.SaveService(ctx, servicestypes.Service{ + ID: 1, + Status: servicestypes.SERVICE_STATUS_ACTIVE, + Address: servicestypes.GetServiceAddress(1).String(), + Tokens: sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(20)), + ), + DelegatorShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("service/1/umilk", sdkmath.LegacyNewDec(100)), + ), + }) + + // Send some funds to the user + suite.fundAccount( + ctx, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100))), + ) + }, + msg: &types.MsgDelegateService{ + ServiceID: 1, + Delegator: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + Amount: sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(0))), + }, + shouldErr: true, + }, + { + name: "valid amount is delegated properly", + store: func(ctx sdk.Context) { + // Create the service + suite.sk.SaveService(ctx, servicestypes.Service{ + ID: 1, + Status: servicestypes.SERVICE_STATUS_ACTIVE, + Address: servicestypes.GetServiceAddress(1).String(), + Tokens: sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(20)), + ), + DelegatorShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("service/1/umilk", sdkmath.LegacyNewDec(100)), + ), + }) + + // Send some funds to the user + suite.fundAccount( + ctx, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100))), + ) + }, + msg: &types.MsgDelegateService{ + ServiceID: 1, + Delegator: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + Amount: sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100))), + }, + shouldErr: false, + expEvents: sdk.Events{ + sdk.NewEvent( + types.EventTypeDelegateService, + sdk.NewAttribute(types.AttributeKeyDelegator, "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4"), + sdk.NewAttribute(types.AttributeKeyServiceID, "1"), + sdk.NewAttribute(sdk.AttributeKeyAmount, "100umilk"), + sdk.NewAttribute(types.AttributeKeyNewShares, "500.000000000000000000service/1/umilk"), + ), + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.setup != nil { + tc.setup() + } + if tc.setupCtx != nil { + ctx = tc.setupCtx(ctx) + } + if tc.store != nil { + tc.store(ctx) + } + + msgServer := keeper.NewMsgServer(suite.k) + _, err := msgServer.DelegateService(ctx, tc.msg) + if tc.shouldErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + for _, event := range tc.expEvents { + suite.Require().Contains(ctx.EventManager().Events(), event) + } + + if tc.check != nil { + tc.check(ctx) + } + } + }) + } +} + +func (suite *KeeperTestSuite) TestMsgServer_UpdateParams() { + testCases := []struct { + name string + setup func() + store func(ctx sdk.Context) + setupCtx func(ctx sdk.Context) sdk.Context + msg *types.MsgUpdateParams + shouldErr bool + expEvents sdk.Events + check func(ctx sdk.Context) + }{ + { + name: "invalid authority return error", + msg: types.NewMsgUpdateParams( + types.DefaultParams(), + "invalid", + ), + shouldErr: true, + }, + { + name: "valid data returns no error", + msg: types.NewMsgUpdateParams( + types.DefaultParams(), + authtypes.NewModuleAddress("gov").String(), + ), + shouldErr: false, + expEvents: sdk.Events{}, + check: func(ctx sdk.Context) { + params := suite.k.GetParams(ctx) + suite.Require().Equal(types.DefaultParams(), params) + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.setup != nil { + tc.setup() + } + if tc.setupCtx != nil { + ctx = tc.setupCtx(ctx) + } + if tc.store != nil { + tc.store(ctx) + } + + msgServer := keeper.NewMsgServer(suite.k) + _, err := msgServer.UpdateParams(ctx, tc.msg) + if tc.shouldErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + for _, event := range tc.expEvents { + suite.Require().Contains(ctx.EventManager().Events(), event) + } + + if tc.check != nil { + tc.check(ctx) + } + } + }) + } +} diff --git a/x/restaking/keeper/operator_restaking.go b/x/restaking/keeper/operator_restaking.go new file mode 100644 index 000000000..e00ab8a62 --- /dev/null +++ b/x/restaking/keeper/operator_restaking.go @@ -0,0 +1,99 @@ +package keeper + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + + operatorstypes "github.com/milkyway-labs/milkyway/x/operators/types" + "github.com/milkyway-labs/milkyway/x/restaking/types" +) + +// SaveOperatorDelegation stores the given operator delegation in the store +func (k *Keeper) SaveOperatorDelegation(ctx sdk.Context, delegation types.OperatorDelegation) { + store := ctx.KVStore(k.storeKey) + + // Marshal and store the delegation + delegationBz := types.MustMarshalOperatorDelegation(k.cdc, delegation) + store.Set(types.UserOperatorDelegationStoreKey(delegation.UserAddress, delegation.OperatorID), delegationBz) + + // Store the delegation in the delegations by operator ID store + store.Set(types.DelegationByOperatorIDStoreKey(delegation.OperatorID, delegation.UserAddress), []byte{}) +} + +// GetOperatorDelegation retrieves the delegation for the given user and operator +// If the delegation does not exist, false is returned instead +func (k *Keeper) GetOperatorDelegation(ctx sdk.Context, operatorID uint32, userAddress string) (types.OperatorDelegation, bool) { + store := ctx.KVStore(k.storeKey) + delegationBz := store.Get(types.UserOperatorDelegationStoreKey(userAddress, operatorID)) + if delegationBz == nil { + return types.OperatorDelegation{}, false + } + + return types.MustUnmarshalOperatorDelegation(k.cdc, delegationBz), true +} + +// AddOperatorTokensAndShares adds the given amount of tokens to the operator and returns the added shares +func (k *Keeper) AddOperatorTokensAndShares( + ctx sdk.Context, operator operatorstypes.Operator, tokensToAdd sdk.Coins, +) (operatorOut operatorstypes.Operator, addedShares sdk.DecCoins, err error) { + + // Update the operator tokens and shares and get the added shares + operator, addedShares = operator.AddTokensFromDelegation(tokensToAdd) + + // Save the operator + k.operatorsKeeper.SaveOperator(ctx, operator) + return operator, addedShares, nil +} + +// -------------------------------------------------------------------------------------------------------------------- + +// DelegateToOperator sends the given amount to the operator account and saves the delegation for the given user +func (k *Keeper) DelegateToOperator(ctx sdk.Context, operatorID uint32, amount sdk.Coins, delegator string) (sdk.DecCoins, error) { + // Get the operator + operator, found := k.operatorsKeeper.GetOperator(ctx, operatorID) + if !found { + return sdk.NewDecCoins(), operatorstypes.ErrOperatorNotFound + } + + // MAke sure the operator is active + if !operator.IsActive() { + return sdk.NewDecCoins(), operatorstypes.ErrOperatorNotActive + } + + return k.PerformDelegation(ctx, types.DelegationData{ + Amount: amount, + Delegator: delegator, + Receiver: &operator, + GetDelegation: func(ctx sdk.Context, receiverID uint32, delegator string) (types.Delegation, bool) { + return k.GetOperatorDelegation(ctx, receiverID, delegator) + }, + BuildDelegation: func(receiverID uint32, delegator string) types.Delegation { + return types.NewOperatorDelegation(receiverID, delegator, sdk.NewDecCoins()) + }, + UpdateDelegation: func(ctx sdk.Context, delegation types.Delegation) (newShares sdk.DecCoins, err error) { + // Calculate the new shares and add the tokens to the operator + _, newShares, err = k.AddOperatorTokensAndShares(ctx, operator, amount) + if err != nil { + return newShares, err + } + + // Update the delegation shares + operatorDelegation, ok := delegation.(types.OperatorDelegation) + if !ok { + return newShares, fmt.Errorf("invalid delegation type: %T", delegation) + } + operatorDelegation.Shares = operatorDelegation.Shares.Add(newShares...) + + // Store the updated delegation + k.SaveOperatorDelegation(ctx, operatorDelegation) + + return newShares, err + }, + Hooks: types.DelegationHooks{ + BeforeDelegationSharesModified: k.BeforeOperatorDelegationSharesModified, + BeforeDelegationCreated: k.BeforeOperatorDelegationCreated, + AfterDelegationModified: k.AfterOperatorDelegationModified, + }, + }) +} diff --git a/x/restaking/keeper/operator_restaking_test.go b/x/restaking/keeper/operator_restaking_test.go new file mode 100644 index 000000000..e05428230 --- /dev/null +++ b/x/restaking/keeper/operator_restaking_test.go @@ -0,0 +1,610 @@ +package keeper_test + +import ( + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + + operatorstypes "github.com/milkyway-labs/milkyway/x/operators/types" + "github.com/milkyway-labs/milkyway/x/restaking/types" +) + +func (suite *KeeperTestSuite) TestKeeper_SaveOperatorDelegation() { + testCases := []struct { + name string + setup func() + store func(ctx sdk.Context) + delegation types.OperatorDelegation + check func(ctx sdk.Context) + }{ + { + name: "operator delegation is stored properly", + delegation: types.NewOperatorDelegation( + 1, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewDecCoins(sdk.NewDecCoinFromDec("umilk", sdkmath.LegacyNewDec(100))), + ), + check: func(ctx sdk.Context) { + store := ctx.KVStore(suite.storeKey) + + // Make sure the user-operator delegation key exists and contains the delegation + delegationBz := store.Get(types.UserOperatorDelegationStoreKey("cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", 1)) + suite.Require().NotNil(delegationBz) + + delegation, err := types.UnmarshalOperatorDelegation(suite.cdc, delegationBz) + suite.Require().NoError(err) + + suite.Require().Equal(types.NewOperatorDelegation( + 1, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewDecCoins(sdk.NewDecCoinFromDec("umilk", sdkmath.LegacyNewDec(100))), + ), delegation) + + // Make sure the operator-user delegation key exists + hasDelegationsByOperatorKey := store.Has(types.DelegationByOperatorIDStoreKey(1, "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4")) + suite.Require().True(hasDelegationsByOperatorKey) + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.setup != nil { + tc.setup() + } + if tc.store != nil { + tc.store(ctx) + } + + suite.k.SaveOperatorDelegation(ctx, tc.delegation) + + if tc.check != nil { + tc.check(ctx) + } + }) + } +} + +func (suite *KeeperTestSuite) TestKeeper_GetOperatorDelegation() { + testCases := []struct { + name string + setup func() + store func(ctx sdk.Context) + operatorID uint32 + userAddress string + expFound bool + expDelegation types.OperatorDelegation + check func(ctx sdk.Context) + }{ + { + name: "not found delegation returns false", + operatorID: 1, + userAddress: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + expFound: false, + }, + { + name: "found delegation is returned properly", + store: func(ctx sdk.Context) { + suite.k.SaveOperatorDelegation(ctx, types.NewOperatorDelegation( + 1, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdk.NewDecCoins(sdk.NewDecCoinFromDec("umilk", sdkmath.LegacyNewDec(100))), + )) + }, + operatorID: 1, + userAddress: "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + expFound: true, + expDelegation: types.NewOperatorDelegation( + 1, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdk.NewDecCoins(sdk.NewDecCoinFromDec("umilk", sdkmath.LegacyNewDec(100))), + ), + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.setup != nil { + tc.setup() + } + if tc.store != nil { + tc.store(ctx) + } + + delegation, found := suite.k.GetOperatorDelegation(ctx, tc.operatorID, tc.userAddress) + if !tc.expFound { + suite.Require().False(found) + } else { + suite.Require().True(found) + suite.Require().Equal(tc.expDelegation, delegation) + } + + if tc.check != nil { + tc.check(ctx) + } + }) + } +} + +func (suite *KeeperTestSuite) TestKeeper_AddOperatorTokensAndShares() { + testCases := []struct { + name string + setup func() + store func(ctx sdk.Context) + operator operatorstypes.Operator + tokensToAdd sdk.Coins + shouldErr bool + expOperator operatorstypes.Operator + expAddedShares sdk.DecCoins + check func(ctx sdk.Context) + }{ + { + name: "adding tokens to an empty operator works properly", + operator: operatorstypes.Operator{ + ID: 1, + Address: operatorstypes.GetOperatorAddress(1).String(), + }, + tokensToAdd: sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100))), + shouldErr: false, + expOperator: operatorstypes.Operator{ + ID: 1, + Address: operatorstypes.GetOperatorAddress(1).String(), + Tokens: sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(100)), + ), + DelegatorShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("operator/1/umilk", sdkmath.LegacyNewDec(100)), + ), + }, + expAddedShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("operator/1/umilk", sdkmath.LegacyNewDec(100)), + ), + }, + { + name: "adding tokens to a non-empty operator works properly", + operator: operatorstypes.Operator{ + ID: 1, + Address: operatorstypes.GetOperatorAddress(1).String(), + Tokens: sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(50)), + ), + DelegatorShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("operator/1/umilk", sdkmath.LegacyNewDec(100)), + ), + }, + tokensToAdd: sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(20))), + shouldErr: false, + expOperator: operatorstypes.Operator{ + ID: 1, + Address: operatorstypes.GetOperatorAddress(1).String(), + Tokens: sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(70)), + ), + DelegatorShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("operator/1/umilk", sdkmath.LegacyNewDec(140)), + ), + }, + expAddedShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("operator/1/umilk", sdkmath.LegacyNewDec(40)), + ), + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.setup != nil { + tc.setup() + } + if tc.store != nil { + tc.store(ctx) + } + + operator, addedShares, err := suite.k.AddOperatorTokensAndShares(ctx, tc.operator, tc.tokensToAdd) + if tc.shouldErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + suite.Require().Equal(tc.expOperator, operator) + suite.Require().Equal(tc.expAddedShares, addedShares) + } + + if tc.check != nil { + tc.check(ctx) + } + }) + } +} + +// -------------------------------------------------------------------------------------------------------------------- + +func (suite *KeeperTestSuite) TestKeeper_DelegateToOperator() { + testCases := []struct { + name string + setup func() + store func(ctx sdk.Context) + operatorID uint32 + amount sdk.Coins + delegator string + shouldErr bool + expShares sdk.DecCoins + check func(ctx sdk.Context) + }{ + { + name: "operator not found returns error", + operatorID: 1, + amount: sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100))), + delegator: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + shouldErr: true, + }, + { + name: "inactive operator returns error", + store: func(ctx sdk.Context) { + suite.ok.SaveOperator(ctx, operatorstypes.Operator{ + ID: 1, + Status: operatorstypes.OPERATOR_STATUS_INACTIVE, + Address: operatorstypes.GetOperatorAddress(1).String(), + Tokens: sdk.NewCoins(), + DelegatorShares: sdk.NewDecCoins(), + }) + }, + operatorID: 1, + amount: sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100))), + delegator: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + shouldErr: true, + }, + { + name: "invalid exchange rate operator returns error", + store: func(ctx sdk.Context) { + suite.ok.SaveOperator(ctx, operatorstypes.Operator{ + ID: 1, + Status: operatorstypes.OPERATOR_STATUS_ACTIVE, + Address: operatorstypes.GetOperatorAddress(1).String(), + Tokens: sdk.NewCoins(), + DelegatorShares: sdk.NewDecCoins(sdk.NewDecCoinFromDec("umilk", sdkmath.LegacyNewDec(100))), + }) + }, + operatorID: 1, + amount: sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100))), + delegator: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + shouldErr: true, + }, + { + name: "invalid delegator address returns error", + store: func(ctx sdk.Context) { + suite.ok.SaveOperator(ctx, operatorstypes.Operator{ + ID: 1, + Address: operatorstypes.GetOperatorAddress(1).String(), + }) + }, + operatorID: 1, + amount: sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100))), + delegator: "invalid", + shouldErr: true, + }, + { + name: "insufficient funds return error", + store: func(ctx sdk.Context) { + // Create the operator + suite.ok.SaveOperator(ctx, operatorstypes.Operator{ + ID: 1, + Address: operatorstypes.GetOperatorAddress(1).String(), + }) + + // Set the next operator id + suite.ok.SetNextOperatorID(ctx, 2) + + // Send some funds to the user + suite.fundAccount( + ctx, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(50))), + ) + }, + operatorID: 1, + amount: sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100))), + delegator: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + shouldErr: true, + }, + { + name: "delegating to an existing operator works properly", + store: func(ctx sdk.Context) { + // Create the operator + suite.ok.SaveOperator(ctx, operatorstypes.Operator{ + ID: 1, + Status: operatorstypes.OPERATOR_STATUS_ACTIVE, + Address: operatorstypes.GetOperatorAddress(1).String(), + Tokens: sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(20)), + ), + DelegatorShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("operator/1/umilk", sdkmath.LegacyNewDec(100)), + ), + }) + + // Set the correct operator tokens amount + suite.fundAccount( + ctx, + operatorstypes.GetOperatorAddress(1).String(), + sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(20))), + ) + + // Set the next operator id + suite.ok.SetNextOperatorID(ctx, 2) + + // Send some funds to the user + suite.fundAccount( + ctx, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100))), + ) + }, + operatorID: 1, + amount: sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100))), + delegator: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + shouldErr: false, + expShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("operator/1/umilk", sdkmath.LegacyNewDec(500)), + ), + check: func(ctx sdk.Context) { + // Make sure the operator now exists + operator, found := suite.ok.GetOperator(ctx, 1) + suite.Require().True(found) + suite.Require().Equal(operatorstypes.Operator{ + ID: 1, + Status: operatorstypes.OPERATOR_STATUS_ACTIVE, + Address: operatorstypes.GetOperatorAddress(1).String(), + Tokens: sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(120)), + ), + DelegatorShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("operator/1/umilk", sdkmath.LegacyNewDec(600)), + ), + }, operator) + + // Make sure the delegation exists + delegation, found := suite.k.GetOperatorDelegation(ctx, 1, "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4") + suite.Require().True(found) + suite.Require().Equal(types.NewOperatorDelegation( + 1, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewDecCoins( + sdk.NewDecCoinFromDec("operator/1/umilk", sdkmath.LegacyNewDec(500)), + ), + ), delegation) + + // Make sure the user balance has been reduced properly + userBalance := suite.bk.GetBalance(ctx, sdk.AccAddress("cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4"), "umilk") + suite.Require().Equal(sdk.NewCoin("umilk", sdkmath.NewInt(0)), userBalance) + + // Make sure the operator account balance has increased properly + operatorBalance := suite.bk.GetBalance(ctx, operatorstypes.GetOperatorAddress(1), "umilk") + suite.Require().Equal(sdk.NewCoin("umilk", sdkmath.NewInt(120)), operatorBalance) + }, + }, + { + name: "delegating another token denom works properly", + store: func(ctx sdk.Context) { + // Create the operator + suite.ok.SaveOperator(ctx, operatorstypes.Operator{ + ID: 1, + Status: operatorstypes.OPERATOR_STATUS_ACTIVE, + Address: operatorstypes.GetOperatorAddress(1).String(), + Tokens: sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(80)), + ), + DelegatorShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("operator/1/umilk", sdkmath.LegacyNewDec(125)), + ), + }) + + // Set the correct operator tokens amount + suite.fundAccount( + ctx, + operatorstypes.GetOperatorAddress(1).String(), + sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(80))), + ) + + // Set the next operator id + suite.ok.SetNextOperatorID(ctx, 2) + + // Save the existing delegation + suite.k.SaveOperatorDelegation(ctx, types.NewOperatorDelegation( + 1, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewDecCoins( + sdk.NewDecCoinFromDec("operator/1/umilk", sdkmath.LegacyNewDec(125)), + ), + )) + + // Send some funds to the user + suite.fundAccount( + ctx, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewCoins(sdk.NewCoin("uinit", sdkmath.NewInt(100))), + ) + }, + operatorID: 1, + amount: sdk.NewCoins(sdk.NewCoin("uinit", sdkmath.NewInt(100))), + delegator: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + shouldErr: false, + expShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("operator/1/uinit", sdkmath.LegacyNewDec(100)), + ), + check: func(ctx sdk.Context) { + // Make sure the operator now exists + operator, found := suite.ok.GetOperator(ctx, 1) + suite.Require().True(found) + suite.Require().Equal(operatorstypes.Operator{ + ID: 1, + Status: operatorstypes.OPERATOR_STATUS_ACTIVE, + Address: operatorstypes.GetOperatorAddress(1).String(), + Tokens: sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(80)), + sdk.NewCoin("uinit", sdkmath.NewInt(100)), + ), + DelegatorShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("operator/1/umilk", sdkmath.LegacyNewDec(125)), + sdk.NewDecCoinFromDec("operator/1/uinit", sdkmath.LegacyNewDec(100)), + ), + }, operator) + + // Make sure the delegation has been updated properly + delegation, found := suite.k.GetOperatorDelegation(ctx, 1, "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4") + suite.Require().True(found) + suite.Require().Equal(types.NewOperatorDelegation( + 1, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewDecCoins( + sdk.NewDecCoinFromDec("operator/1/umilk", sdkmath.LegacyNewDec(125)), + sdk.NewDecCoinFromDec("operator/1/uinit", sdkmath.LegacyNewDec(100)), + ), + ), delegation) + + // Make sure the user balance has been reduced properly + userBalance := suite.bk.GetBalance(ctx, sdk.AccAddress("cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4"), "umilk") + suite.Require().Equal(sdk.NewCoin("umilk", sdkmath.NewInt(0)), userBalance) + + // Make sure the operator account balance has increased properly + operatorBalance := suite.bk.GetAllBalances(ctx, operatorstypes.GetOperatorAddress(1)) + suite.Require().Equal(sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(80)), + sdk.NewCoin("uinit", sdkmath.NewInt(100)), + ), operatorBalance) + }, + }, + { + name: "delegating more tokens works properly", + store: func(ctx sdk.Context) { + // Create the operator + suite.ok.SaveOperator(ctx, operatorstypes.Operator{ + ID: 1, + Status: operatorstypes.OPERATOR_STATUS_ACTIVE, + Address: operatorstypes.GetOperatorAddress(1).String(), + Tokens: sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(80)), + sdk.NewCoin("uinit", sdkmath.NewInt(75)), + ), + DelegatorShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("operator/1/umilk", sdkmath.LegacyNewDec(125)), + sdk.NewDecCoinFromDec("operator/1/uinit", sdkmath.LegacyNewDec(200)), + ), + }) + + // Set the correct operator tokens amount + suite.fundAccount( + ctx, + operatorstypes.GetOperatorAddress(1).String(), + sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(80)), + sdk.NewCoin("uinit", sdkmath.NewInt(75)), + ), + ) + + // Set the next operator id + suite.ok.SetNextOperatorID(ctx, 2) + + // Save the existing delegation + suite.k.SaveOperatorDelegation(ctx, types.NewOperatorDelegation( + 1, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewDecCoins( + sdk.NewDecCoinFromDec("operator/1/umilk", sdkmath.LegacyNewDec(100)), + sdk.NewDecCoinFromDec("operator/1/uinit", sdkmath.LegacyNewDec(60)), + ), + )) + + // Send some funds to the user + suite.fundAccount( + ctx, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(100)), + sdk.NewCoin("uinit", sdkmath.NewInt(225)), + ), + ) + }, + operatorID: 1, + amount: sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(100)), + sdk.NewCoin("uinit", sdkmath.NewInt(225)), + ), + delegator: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + shouldErr: false, + expShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("operator/1/umilk", sdkmath.LegacyNewDecWithPrec(15625, 2)), + sdk.NewDecCoinFromDec("operator/1/uinit", sdkmath.LegacyNewDec(600)), + ), + check: func(ctx sdk.Context) { + // Make sure the operator now exists + operator, found := suite.ok.GetOperator(ctx, 1) + suite.Require().True(found) + suite.Require().Equal(operatorstypes.Operator{ + ID: 1, + Status: operatorstypes.OPERATOR_STATUS_ACTIVE, + Address: operatorstypes.GetOperatorAddress(1).String(), + Tokens: sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(180)), + sdk.NewCoin("uinit", sdkmath.NewInt(300)), + ), + DelegatorShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("operator/1/umilk", sdkmath.LegacyNewDecWithPrec(28125, 2)), + sdk.NewDecCoinFromDec("operator/1/uinit", sdkmath.LegacyNewDec(800)), + ), + }, operator) + + // Make sure the delegation has been updated properly + delegation, found := suite.k.GetOperatorDelegation(ctx, 1, "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4") + suite.Require().True(found) + suite.Require().Equal(types.NewOperatorDelegation( + 1, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewDecCoins( + sdk.NewDecCoinFromDec("operator/1/umilk", sdkmath.LegacyNewDecWithPrec(25625, 2)), // 100 (existing) + 156.25 (new) + sdk.NewDecCoinFromDec("operator/1/uinit", sdkmath.LegacyNewDec(660)), // 60 (existing) + 600 (new) + ), + ), delegation) + + // Make sure the user balance has been reduced properly + userBalance := suite.bk.GetBalance(ctx, sdk.AccAddress("cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4"), "umilk") + suite.Require().Equal(sdk.NewCoin("umilk", sdkmath.NewInt(0)), userBalance) + + // Make sure the operator account balance has increased properly + operatorBalance := suite.bk.GetAllBalances(ctx, operatorstypes.GetOperatorAddress(1)) + suite.Require().Equal(sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(180)), + sdk.NewCoin("uinit", sdkmath.NewInt(300)), + ), operatorBalance) + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.setup != nil { + tc.setup() + } + if tc.store != nil { + tc.store(ctx) + } + + shares, err := suite.k.DelegateToOperator(ctx, tc.operatorID, tc.amount, tc.delegator) + if tc.shouldErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + suite.Require().Equal(tc.expShares, shares) + } + + if tc.check != nil { + tc.check(ctx) + } + }) + } +} diff --git a/x/restaking/keeper/params.go b/x/restaking/keeper/params.go new file mode 100644 index 000000000..b373b730a --- /dev/null +++ b/x/restaking/keeper/params.go @@ -0,0 +1,25 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/milkyway-labs/milkyway/x/restaking/types" +) + +// SetParams sets module parameters +func (k *Keeper) SetParams(ctx sdk.Context, params types.Params) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshal(¶ms) + store.Set(types.ParamsKey, bz) +} + +// GetParams returns the module parameters +func (k *Keeper) GetParams(ctx sdk.Context) (p types.Params) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.ParamsKey) + if bz == nil { + return p + } + k.cdc.MustUnmarshal(bz, &p) + return p +} diff --git a/x/restaking/keeper/pool_restaking.go b/x/restaking/keeper/pool_restaking.go new file mode 100644 index 000000000..5396dd458 --- /dev/null +++ b/x/restaking/keeper/pool_restaking.go @@ -0,0 +1,99 @@ +package keeper + +import ( + "fmt" + + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + + poolstypes "github.com/milkyway-labs/milkyway/x/pools/types" + "github.com/milkyway-labs/milkyway/x/restaking/types" +) + +// SavePoolDelegation stores the given pool delegation in the store +func (k *Keeper) SavePoolDelegation(ctx sdk.Context, delegation types.PoolDelegation) { + store := ctx.KVStore(k.storeKey) + + // Marshal and store the delegation + delegationBz := types.MustMarshalPoolDelegation(k.cdc, delegation) + store.Set(types.UserPoolDelegationStoreKey(delegation.UserAddress, delegation.PoolID), delegationBz) + + // Store the delegation in the delegations by pool ID store + store.Set(types.DelegationByPoolIDStoreKey(delegation.PoolID, delegation.UserAddress), []byte{}) +} + +// GetPoolDelegation retrieves the delegation for the given user and pool +// If the delegation does not exist, false is returned instead +func (k *Keeper) GetPoolDelegation(ctx sdk.Context, poolID uint32, userAddress string) (types.PoolDelegation, bool) { + store := ctx.KVStore(k.storeKey) + delegationBz := store.Get(types.UserPoolDelegationStoreKey(userAddress, poolID)) + if delegationBz == nil { + return types.PoolDelegation{}, false + } + + return types.MustUnmarshalPoolDelegation(k.cdc, delegationBz), true +} + +// AddPoolTokensAndShares adds the given amount of tokens to the pool and returns the added shares +func (k *Keeper) AddPoolTokensAndShares( + ctx sdk.Context, pool poolstypes.Pool, tokensToAdd sdkmath.Int, +) (poolOut poolstypes.Pool, addedShares sdkmath.LegacyDec, err error) { + + // Update the pool tokens and shares and get the added shares + pool, addedShares = pool.AddTokensFromDelegation(tokensToAdd) + + // Save the pool + err = k.poolsKeeper.SavePool(ctx, pool) + return pool, addedShares, err +} + +// -------------------------------------------------------------------------------------------------------------------- + +// DelegateToPool sends the given amount to the pool account and saves the delegation for the given user +func (k *Keeper) DelegateToPool(ctx sdk.Context, amount sdk.Coin, delegator string) (sdk.DecCoins, error) { + // Get or create the pool for the given amount denom + pool, err := k.poolsKeeper.CreateOrGetPoolByDenom(ctx, amount.Denom) + if err != nil { + return sdk.NewDecCoins(), err + } + + // Get the amount to be bonded + coins := sdk.NewCoins(sdk.NewCoin(pool.Denom, amount.Amount)) + + return k.PerformDelegation(ctx, types.DelegationData{ + Amount: coins, + Delegator: delegator, + Receiver: &pool, + GetDelegation: func(ctx sdk.Context, receiverID uint32, delegator string) (types.Delegation, bool) { + return k.GetPoolDelegation(ctx, receiverID, delegator) + }, + BuildDelegation: func(receiverID uint32, delegator string) types.Delegation { + return types.NewPoolDelegation(receiverID, delegator, sdkmath.LegacyZeroDec()) + }, + UpdateDelegation: func(ctx sdk.Context, delegation types.Delegation) (sdk.DecCoins, error) { + // Calculate the new shares and add the tokens to the pool + _, newShares, err := k.AddPoolTokensAndShares(ctx, pool, amount.Amount) + if err != nil { + return nil, err + } + + // Update the delegation shares + poolDelegation, ok := delegation.(types.PoolDelegation) + if !ok { + return nil, fmt.Errorf("invalid delegation type: %T", delegation) + } + poolDelegation.Shares = poolDelegation.Shares.Add(newShares) + + // Store the updated delegation + k.SavePoolDelegation(ctx, poolDelegation) + + sharesDenom := pool.GetSharesDenom(amount.Denom) + return sdk.NewDecCoins(sdk.NewDecCoinFromDec(sharesDenom, newShares)), err + }, + Hooks: types.DelegationHooks{ + BeforeDelegationSharesModified: k.BeforePoolDelegationSharesModified, + BeforeDelegationCreated: k.BeforePoolDelegationCreated, + AfterDelegationModified: k.AfterPoolDelegationModified, + }, + }) +} diff --git a/x/restaking/keeper/pool_restaking_test.go b/x/restaking/keeper/pool_restaking_test.go new file mode 100644 index 000000000..15eb817d5 --- /dev/null +++ b/x/restaking/keeper/pool_restaking_test.go @@ -0,0 +1,475 @@ +package keeper_test + +import ( + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + + poolstypes "github.com/milkyway-labs/milkyway/x/pools/types" + "github.com/milkyway-labs/milkyway/x/restaking/types" +) + +func (suite *KeeperTestSuite) TestKeeper_SavePoolDelegation() { + testCases := []struct { + name string + setup func() + store func(ctx sdk.Context) + delegation types.PoolDelegation + check func(ctx sdk.Context) + }{ + { + name: "pool delegation is stored properly", + delegation: types.NewPoolDelegation( + 1, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdkmath.LegacyNewDec(100), + ), + check: func(ctx sdk.Context) { + store := ctx.KVStore(suite.storeKey) + + // Make sure the user-pool delegation key exists and contains the delegation + delegationBz := store.Get(types.UserPoolDelegationStoreKey("cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", 1)) + suite.Require().NotNil(delegationBz) + + delegation, err := types.UnmarshalPoolDelegation(suite.cdc, delegationBz) + suite.Require().NoError(err) + + suite.Require().Equal(types.NewPoolDelegation( + 1, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdkmath.LegacyNewDec(100), + ), delegation) + + // Make sure the pool-user delegation key exists + hasDelegationsByPoolKey := store.Has(types.DelegationByPoolIDStoreKey(1, "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4")) + suite.Require().True(hasDelegationsByPoolKey) + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.setup != nil { + tc.setup() + } + if tc.store != nil { + tc.store(ctx) + } + + suite.k.SavePoolDelegation(ctx, tc.delegation) + + if tc.check != nil { + tc.check(ctx) + } + }) + } +} + +func (suite *KeeperTestSuite) TestKeeper_GetPoolDelegation() { + testCases := []struct { + name string + setup func() + store func(ctx sdk.Context) + poolID uint32 + userAddress string + expFound bool + expDelegation types.PoolDelegation + check func(ctx sdk.Context) + }{ + { + name: "not found delegation returns false", + poolID: 1, + userAddress: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + expFound: false, + }, + { + name: "found delegation is returned properly", + store: func(ctx sdk.Context) { + suite.k.SavePoolDelegation(ctx, types.NewPoolDelegation( + 1, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdkmath.LegacyNewDec(100), + )) + }, + poolID: 1, + userAddress: "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + expFound: true, + expDelegation: types.NewPoolDelegation( + 1, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdkmath.LegacyNewDec(100), + ), + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.setup != nil { + tc.setup() + } + if tc.store != nil { + tc.store(ctx) + } + + delegation, found := suite.k.GetPoolDelegation(ctx, tc.poolID, tc.userAddress) + if !tc.expFound { + suite.Require().False(found) + } else { + suite.Require().True(found) + suite.Require().Equal(tc.expDelegation, delegation) + } + + if tc.check != nil { + tc.check(ctx) + } + }) + } +} + +func (suite *KeeperTestSuite) TestKeeper_AddPoolTokensAndShares() { + testCases := []struct { + name string + setup func() + store func(ctx sdk.Context) + pool poolstypes.Pool + tokensToAdd sdkmath.Int + shouldErr bool + expPool poolstypes.Pool + expAddedShares sdkmath.LegacyDec + check func(ctx sdk.Context) + }{ + { + name: "adding tokens to an empty pool works properly", + pool: poolstypes.NewPool(1, "umilk"), + tokensToAdd: sdkmath.NewInt(100), + shouldErr: false, + expPool: poolstypes.Pool{ + ID: 1, + Denom: "umilk", + Address: poolstypes.GetPoolAddress(1).String(), + Tokens: sdkmath.NewInt(100), + DelegatorShares: sdkmath.LegacyNewDec(100), + }, + expAddedShares: sdkmath.LegacyNewDec(100), + }, + { + name: "adding tokens to a non-empty pool works properly", + pool: poolstypes.Pool{ + ID: 1, + Denom: "umilk", + Address: poolstypes.GetPoolAddress(1).String(), + Tokens: sdkmath.NewInt(50), + DelegatorShares: sdkmath.LegacyNewDec(100), + }, + tokensToAdd: sdkmath.NewInt(20), + shouldErr: false, + expPool: poolstypes.Pool{ + ID: 1, + Denom: "umilk", + Address: poolstypes.GetPoolAddress(1).String(), + Tokens: sdkmath.NewInt(70), + DelegatorShares: sdkmath.LegacyNewDec(140), + }, + expAddedShares: sdkmath.LegacyNewDec(40), + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.setup != nil { + tc.setup() + } + if tc.store != nil { + tc.store(ctx) + } + + pool, addedShares, err := suite.k.AddPoolTokensAndShares(ctx, tc.pool, tc.tokensToAdd) + if tc.shouldErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + suite.Require().Equal(tc.expPool, pool) + suite.Require().Equal(tc.expAddedShares, addedShares) + } + + if tc.check != nil { + tc.check(ctx) + } + }) + } +} + +// -------------------------------------------------------------------------------------------------------------------- + +func (suite *KeeperTestSuite) TestKeeper_DelegateToPool() { + testCases := []struct { + name string + setup func() + store func(ctx sdk.Context) + amount sdk.Coin + delegator string + shouldErr bool + expShares sdk.DecCoins + check func(ctx sdk.Context) + }{ + { + name: "invalid exchange rate pool returns error", + store: func(ctx sdk.Context) { + err := suite.pk.SavePool(ctx, poolstypes.Pool{ + ID: 1, + Denom: "umilk", + Address: poolstypes.GetPoolAddress(1).String(), + Tokens: sdkmath.ZeroInt(), + DelegatorShares: sdkmath.LegacyNewDec(100), + }) + suite.Require().NoError(err) + }, + amount: sdk.NewCoin("umilk", sdkmath.NewInt(100)), + delegator: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + shouldErr: true, + }, + { + name: "invalid delegator address returns error", + store: func(ctx sdk.Context) { + err := suite.pk.SavePool(ctx, poolstypes.NewPool(1, "umilk")) + suite.Require().NoError(err) + }, + amount: sdk.NewCoin("umilk", sdkmath.NewInt(100)), + delegator: "invalid", + shouldErr: true, + }, + { + name: "insufficient funds return error", + store: func(ctx sdk.Context) { + // Create the pool + err := suite.pk.SavePool(ctx, poolstypes.NewPool(1, "umilk")) + suite.Require().NoError(err) + + // Set the next pool id + suite.pk.SetNextPoolID(ctx, 2) + + // Send some funds to the user + suite.fundAccount( + ctx, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(50))), + ) + }, + amount: sdk.NewCoin("umilk", sdkmath.NewInt(100)), + delegator: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + shouldErr: true, + }, + { + name: "delegating to a non-existing pool works properly", + store: func(ctx sdk.Context) { + // Set the next pool id + suite.pk.SetNextPoolID(ctx, 1) + + // Send some funds to the user + suite.fundAccount( + ctx, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100))), + ) + }, + amount: sdk.NewCoin("umilk", sdkmath.NewInt(100)), + delegator: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + shouldErr: false, + expShares: sdk.NewDecCoins(sdk.NewDecCoinFromDec("pool/1/umilk", sdkmath.LegacyNewDec(100))), + check: func(ctx sdk.Context) { + // Make sure the pool now exists + pool, found := suite.pk.GetPool(ctx, 1) + suite.Require().True(found) + suite.Require().Equal(poolstypes.Pool{ + ID: 1, + Denom: "umilk", + Address: poolstypes.GetPoolAddress(1).String(), + Tokens: sdkmath.NewInt(100), + DelegatorShares: sdkmath.LegacyNewDec(100), + }, pool) + + // Make sure the delegation exists + delegation, found := suite.k.GetPoolDelegation(ctx, 1, "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4") + suite.Require().True(found) + suite.Require().Equal(types.NewPoolDelegation( + 1, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdkmath.LegacyNewDec(100), + ), delegation) + + // Make sure the user balance has been reduced properly + userBalance := suite.bk.GetBalance(ctx, sdk.AccAddress("cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4"), "umilk") + suite.Require().Equal(sdk.NewCoin("umilk", sdkmath.NewInt(0)), userBalance) + + // Make sure the pool account balance has increased properly + poolBalance := suite.bk.GetBalance(ctx, poolstypes.GetPoolAddress(1), "umilk") + suite.Require().Equal(sdk.NewCoin("umilk", sdkmath.NewInt(100)), poolBalance) + }, + }, + { + name: "delegating to an existing pool works properly", + store: func(ctx sdk.Context) { + // Create the pool + err := suite.pk.SavePool(ctx, poolstypes.Pool{ + ID: 1, + Denom: "umilk", + Address: poolstypes.GetPoolAddress(1).String(), + Tokens: sdkmath.NewInt(20), + DelegatorShares: sdkmath.LegacyNewDec(100), + }) + suite.Require().NoError(err) + + // Set the correct pool tokens amount + suite.fundAccount( + ctx, + poolstypes.GetPoolAddress(1).String(), + sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(20))), + ) + + // Set the next pool id + suite.pk.SetNextPoolID(ctx, 2) + + // Send some funds to the user + suite.fundAccount( + ctx, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100))), + ) + }, + amount: sdk.NewCoin("umilk", sdkmath.NewInt(100)), + delegator: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + shouldErr: false, + expShares: sdk.NewDecCoins(sdk.NewDecCoinFromDec("pool/1/umilk", sdkmath.LegacyNewDec(500))), + check: func(ctx sdk.Context) { + // Make sure the pool now exists + pool, found := suite.pk.GetPool(ctx, 1) + suite.Require().True(found) + suite.Require().Equal(poolstypes.Pool{ + ID: 1, + Denom: "umilk", + Address: poolstypes.GetPoolAddress(1).String(), + Tokens: sdkmath.NewInt(120), + DelegatorShares: sdkmath.LegacyNewDec(600), + }, pool) + + // Make sure the delegation exists + delegation, found := suite.k.GetPoolDelegation(ctx, 1, "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4") + suite.Require().True(found) + suite.Require().Equal(types.NewPoolDelegation( + 1, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdkmath.LegacyNewDec(500), + ), delegation) + + // Make sure the user balance has been reduced properly + userBalance := suite.bk.GetBalance(ctx, sdk.AccAddress("cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4"), "umilk") + suite.Require().Equal(sdk.NewCoin("umilk", sdkmath.NewInt(0)), userBalance) + + // Make sure the pool account balance has increased properly + poolBalance := suite.bk.GetBalance(ctx, poolstypes.GetPoolAddress(1), "umilk") + suite.Require().Equal(sdk.NewCoin("umilk", sdkmath.NewInt(120)), poolBalance) + }, + }, + { + name: "delegating more tokens works properly", + store: func(ctx sdk.Context) { + // Create the pool + err := suite.pk.SavePool(ctx, poolstypes.Pool{ + ID: 1, + Denom: "umilk", + Address: poolstypes.GetPoolAddress(1).String(), + Tokens: sdkmath.NewInt(80), + DelegatorShares: sdkmath.LegacyNewDec(125), + }) + suite.Require().NoError(err) + + // Set the correct pool tokens amount + suite.fundAccount( + ctx, + poolstypes.GetPoolAddress(1).String(), + sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(80))), + ) + + // Set the next pool id + suite.pk.SetNextPoolID(ctx, 2) + + // Save the existing delegation + suite.k.SavePoolDelegation(ctx, types.NewPoolDelegation( + 1, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdkmath.LegacyNewDec(100), + )) + + // Send some funds to the user + suite.fundAccount( + ctx, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100))), + ) + }, + amount: sdk.NewCoin("umilk", sdkmath.NewInt(100)), + delegator: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + shouldErr: false, + expShares: sdk.NewDecCoins(sdk.NewDecCoinFromDec("pool/1/umilk", sdkmath.LegacyNewDecWithPrec(15625, 2))), + check: func(ctx sdk.Context) { + // Make sure the pool now exists + pool, found := suite.pk.GetPool(ctx, 1) + suite.Require().True(found) + suite.Require().Equal(poolstypes.Pool{ + ID: 1, + Denom: "umilk", + Address: poolstypes.GetPoolAddress(1).String(), + Tokens: sdkmath.NewInt(180), + DelegatorShares: sdkmath.LegacyNewDecWithPrec(28125, 2), + }, pool) + + // Make sure the delegation has been updated properly + delegation, found := suite.k.GetPoolDelegation(ctx, 1, "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4") + suite.Require().True(found) + suite.Require().Equal(types.NewPoolDelegation( + 1, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdkmath.LegacyNewDecWithPrec(25625, 2), // 100 (existing) + 156.25 (new) + ), delegation) + + // Make sure the user balance has been reduced properly + userBalance := suite.bk.GetBalance(ctx, sdk.AccAddress("cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4"), "umilk") + suite.Require().Equal(sdk.NewCoin("umilk", sdkmath.NewInt(0)), userBalance) + + // Make sure the pool account balance has increased properly + poolBalance := suite.bk.GetBalance(ctx, poolstypes.GetPoolAddress(1), "umilk") + suite.Require().Equal(sdk.NewCoin("umilk", sdkmath.NewInt(180)), poolBalance) + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.setup != nil { + tc.setup() + } + if tc.store != nil { + tc.store(ctx) + } + + shares, err := suite.k.DelegateToPool(ctx, tc.amount, tc.delegator) + if tc.shouldErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + suite.Require().Equal(tc.expShares, shares) + } + + if tc.check != nil { + tc.check(ctx) + } + }) + } +} diff --git a/x/restaking/keeper/restaking.go b/x/restaking/keeper/restaking.go new file mode 100644 index 000000000..3a0b7e2e2 --- /dev/null +++ b/x/restaking/keeper/restaking.go @@ -0,0 +1,74 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/milkyway-labs/milkyway/x/restaking/types" +) + +// PerformDelegation performs a delegation of the given amount from the delegator to the receiver. +// It sends the coins to the receiver address and updates the delegation object and returns the new +// shares of the delegation. +// NOTE: This is done so that if we implement other delegation types in the future we can have a single +// function that performs common operations for all of them. +func (k *Keeper) PerformDelegation(ctx sdk.Context, data types.DelegationData) (sdk.DecCoins, error) { + // Get the data + receiver := data.Receiver + delegator := data.Delegator + hooks := data.Hooks + + // In some situations, the exchange rate becomes invalid, e.g. if + // the receives loses all tokens due to slashing. In this case, + // make all future delegations invalid. + if receiver.InvalidExRate() { + return nil, types.ErrDelegatorShareExRateInvalid + } + + // Get or create the delegation object and call the appropriate hook if present + delegation, found := data.GetDelegation(ctx, receiver.GetID(), delegator) + + if found { + // Delegation was found + err := hooks.BeforeDelegationSharesModified(ctx, receiver.GetID(), delegator) + if err != nil { + return nil, err + } + } else { + // Delegation was not found + delegation = data.BuildDelegation(receiver.GetID(), delegator) + err := hooks.BeforeDelegationCreated(ctx, receiver.GetID(), delegator) + if err != nil { + return nil, err + } + } + + // Convert the addresses to sdk.AccAddress + delegatorAddress, err := k.accountKeeper.AddressCodec().StringToBytes(delegator) + if err != nil { + return nil, err + } + receiverAddress, err := k.accountKeeper.AddressCodec().StringToBytes(receiver.GetAddress()) + if err != nil { + return nil, err + } + + // Send the coins to the receiver address + err = k.bankKeeper.SendCoins(ctx, delegatorAddress, receiverAddress, data.Amount) + if err != nil { + return nil, err + } + + // Update the delegation + newShares, err := data.UpdateDelegation(ctx, delegation) + if err != nil { + return nil, err + } + + // Call the after-modification hook + err = hooks.AfterDelegationModified(ctx, receiver.GetID(), delegator) + if err != nil { + return nil, err + } + + return newShares, nil +} diff --git a/x/restaking/keeper/service_restaking.go b/x/restaking/keeper/service_restaking.go new file mode 100644 index 000000000..07fb87d05 --- /dev/null +++ b/x/restaking/keeper/service_restaking.go @@ -0,0 +1,99 @@ +package keeper + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/milkyway-labs/milkyway/x/restaking/types" + servicestypes "github.com/milkyway-labs/milkyway/x/services/types" +) + +// SaveServiceDelegation stores the given service delegation in the store +func (k *Keeper) SaveServiceDelegation(ctx sdk.Context, delegation types.ServiceDelegation) { + store := ctx.KVStore(k.storeKey) + + // Marshal and store the delegation + delegationBz := types.MustMarshalServiceDelegation(k.cdc, delegation) + store.Set(types.UserServiceDelegationStoreKey(delegation.UserAddress, delegation.ServiceID), delegationBz) + + // Store the delegation in the delegations by service ID store + store.Set(types.DelegationByServiceIDStoreKey(delegation.ServiceID, delegation.UserAddress), []byte{}) +} + +// GetServiceDelegation retrieves the delegation for the given user and service +// If the delegation does not exist, false is returned instead +func (k *Keeper) GetServiceDelegation(ctx sdk.Context, serviceID uint32, userAddress string) (types.ServiceDelegation, bool) { + store := ctx.KVStore(k.storeKey) + delegationBz := store.Get(types.UserServiceDelegationStoreKey(userAddress, serviceID)) + if delegationBz == nil { + return types.ServiceDelegation{}, false + } + + return types.MustUnmarshalServiceDelegation(k.cdc, delegationBz), true +} + +// AddServiceTokensAndShares adds the given amount of tokens to the service and returns the added shares +func (k *Keeper) AddServiceTokensAndShares( + ctx sdk.Context, service servicestypes.Service, tokensToAdd sdk.Coins, +) (serviceOut servicestypes.Service, addedShares sdk.DecCoins, err error) { + + // Update the service tokens and shares and get the added shares + service, addedShares = service.AddTokensFromDelegation(tokensToAdd) + + // Save the service + k.servicesKeeper.SaveService(ctx, service) + return service, addedShares, nil +} + +// -------------------------------------------------------------------------------------------------------------------- + +// DelegateToService sends the given amount to the service account and saves the delegation for the given user +func (k *Keeper) DelegateToService(ctx sdk.Context, serviceID uint32, amount sdk.Coins, delegator string) (sdk.DecCoins, error) { + // Get the service + service, found := k.servicesKeeper.GetService(ctx, serviceID) + if !found { + return sdk.NewDecCoins(), servicestypes.ErrServiceNotFound + } + + // Make sure the service is active + if !service.IsActive() { + return sdk.NewDecCoins(), servicestypes.ErrServiceNotActive + } + + return k.PerformDelegation(ctx, types.DelegationData{ + Amount: amount, + Delegator: delegator, + Receiver: &service, + GetDelegation: func(ctx sdk.Context, receiverID uint32, delegator string) (types.Delegation, bool) { + return k.GetServiceDelegation(ctx, receiverID, delegator) + }, + BuildDelegation: func(receiverID uint32, delegator string) types.Delegation { + return types.NewServiceDelegation(receiverID, delegator, sdk.NewDecCoins()) + }, + UpdateDelegation: func(ctx sdk.Context, delegation types.Delegation) (newShares sdk.DecCoins, err error) { + // Calculate the new shares and add the tokens to the service + _, newShares, err = k.AddServiceTokensAndShares(ctx, service, amount) + if err != nil { + return newShares, err + } + + // Update the delegation shares + serviceDelegation, ok := delegation.(types.ServiceDelegation) + if !ok { + return newShares, fmt.Errorf("invalid delegation type: %T", delegation) + } + serviceDelegation.Shares = serviceDelegation.Shares.Add(newShares...) + + // Store the updated delegation + k.SaveServiceDelegation(ctx, serviceDelegation) + + return newShares, err + }, + Hooks: types.DelegationHooks{ + BeforeDelegationSharesModified: k.BeforeServiceDelegationSharesModified, + BeforeDelegationCreated: k.BeforeServiceDelegationCreated, + AfterDelegationModified: k.AfterServiceDelegationModified, + }, + }) +} diff --git a/x/restaking/keeper/service_restaking_test.go b/x/restaking/keeper/service_restaking_test.go new file mode 100644 index 000000000..f2c02e7c2 --- /dev/null +++ b/x/restaking/keeper/service_restaking_test.go @@ -0,0 +1,606 @@ +package keeper_test + +import ( + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/milkyway-labs/milkyway/x/restaking/types" + servicestypes "github.com/milkyway-labs/milkyway/x/services/types" +) + +func (suite *KeeperTestSuite) TestKeeper_SaveServiceDelegation() { + testCases := []struct { + name string + setup func() + store func(ctx sdk.Context) + delegation types.ServiceDelegation + check func(ctx sdk.Context) + }{ + { + name: "service delegation is stored properly", + delegation: types.NewServiceDelegation( + 1, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewDecCoins(sdk.NewDecCoinFromDec("umilk", sdkmath.LegacyNewDec(100))), + ), + check: func(ctx sdk.Context) { + store := ctx.KVStore(suite.storeKey) + + // Make sure the user-service delegation key exists and contains the delegation + delegationBz := store.Get(types.UserServiceDelegationStoreKey("cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", 1)) + suite.Require().NotNil(delegationBz) + + delegation, err := types.UnmarshalServiceDelegation(suite.cdc, delegationBz) + suite.Require().NoError(err) + + suite.Require().Equal(types.NewServiceDelegation( + 1, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewDecCoins(sdk.NewDecCoinFromDec("umilk", sdkmath.LegacyNewDec(100))), + ), delegation) + + // Make sure the service-user delegation key exists + hasDelegationsByServiceKey := store.Has(types.DelegationByServiceIDStoreKey(1, "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4")) + suite.Require().True(hasDelegationsByServiceKey) + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.setup != nil { + tc.setup() + } + if tc.store != nil { + tc.store(ctx) + } + + suite.k.SaveServiceDelegation(ctx, tc.delegation) + + if tc.check != nil { + tc.check(ctx) + } + }) + } +} + +func (suite *KeeperTestSuite) TestKeeper_GetServiceDelegation() { + testCases := []struct { + name string + setup func() + store func(ctx sdk.Context) + serviceID uint32 + userAddress string + expFound bool + expDelegation types.ServiceDelegation + check func(ctx sdk.Context) + }{ + { + name: "not found delegation returns false", + serviceID: 1, + userAddress: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + expFound: false, + }, + { + name: "found delegation is returned properly", + store: func(ctx sdk.Context) { + suite.k.SaveServiceDelegation(ctx, types.NewServiceDelegation( + 1, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdk.NewDecCoins(sdk.NewDecCoinFromDec("umilk", sdkmath.LegacyNewDec(100))), + )) + }, + serviceID: 1, + userAddress: "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + expFound: true, + expDelegation: types.NewServiceDelegation( + 1, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdk.NewDecCoins(sdk.NewDecCoinFromDec("umilk", sdkmath.LegacyNewDec(100))), + ), + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.setup != nil { + tc.setup() + } + if tc.store != nil { + tc.store(ctx) + } + + delegation, found := suite.k.GetServiceDelegation(ctx, tc.serviceID, tc.userAddress) + if !tc.expFound { + suite.Require().False(found) + } else { + suite.Require().True(found) + suite.Require().Equal(tc.expDelegation, delegation) + } + + if tc.check != nil { + tc.check(ctx) + } + }) + } +} + +func (suite *KeeperTestSuite) TestKeeper_AddServiceTokensAndShares() { + testCases := []struct { + name string + setup func() + store func(ctx sdk.Context) + service servicestypes.Service + tokensToAdd sdk.Coins + shouldErr bool + expService servicestypes.Service + expAddedShares sdk.DecCoins + check func(ctx sdk.Context) + }{ + { + name: "adding tokens to an empty service works properly", + service: servicestypes.Service{ + ID: 1, + Address: servicestypes.GetServiceAddress(1).String(), + }, + tokensToAdd: sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100))), + shouldErr: false, + expService: servicestypes.Service{ + ID: 1, + Address: servicestypes.GetServiceAddress(1).String(), + Tokens: sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(100)), + ), + DelegatorShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("service/1/umilk", sdkmath.LegacyNewDec(100)), + ), + }, + expAddedShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("service/1/umilk", sdkmath.LegacyNewDec(100)), + ), + }, + { + name: "adding tokens to a non-empty service works properly", + service: servicestypes.Service{ + ID: 1, + Address: servicestypes.GetServiceAddress(1).String(), + Tokens: sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(50)), + ), + DelegatorShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("service/1/umilk", sdkmath.LegacyNewDec(100)), + ), + }, + tokensToAdd: sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(20))), + shouldErr: false, + expService: servicestypes.Service{ + ID: 1, + Address: servicestypes.GetServiceAddress(1).String(), + Tokens: sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(70)), + ), + DelegatorShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("service/1/umilk", sdkmath.LegacyNewDec(140)), + ), + }, + expAddedShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("service/1/umilk", sdkmath.LegacyNewDec(40)), + ), + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.setup != nil { + tc.setup() + } + if tc.store != nil { + tc.store(ctx) + } + + service, addedShares, err := suite.k.AddServiceTokensAndShares(ctx, tc.service, tc.tokensToAdd) + if tc.shouldErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + suite.Require().Equal(tc.expService, service) + suite.Require().Equal(tc.expAddedShares, addedShares) + } + + if tc.check != nil { + tc.check(ctx) + } + }) + } +} + +// -------------------------------------------------------------------------------------------------------------------- + +func (suite *KeeperTestSuite) TestKeeper_DelegateToService() { + testCases := []struct { + name string + setup func() + store func(ctx sdk.Context) + serviceID uint32 + amount sdk.Coins + delegator string + shouldErr bool + expShares sdk.DecCoins + check func(ctx sdk.Context) + }{ + { + name: "service not found returns error", + serviceID: 1, + amount: sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100))), + delegator: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + shouldErr: true, + }, + { + name: "inactive service returns error", + store: func(ctx sdk.Context) { + suite.sk.SaveService(ctx, servicestypes.Service{ + ID: 1, + Status: servicestypes.SERVICE_STATUS_INACTIVE, + Address: servicestypes.GetServiceAddress(1).String(), + Tokens: sdk.NewCoins(), + DelegatorShares: sdk.NewDecCoins(), + }) + }, + serviceID: 1, + amount: sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100))), + delegator: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + shouldErr: true, + }, + { + name: "invalid exchange rate service returns error", + store: func(ctx sdk.Context) { + suite.sk.SaveService(ctx, servicestypes.Service{ + ID: 1, + Status: servicestypes.SERVICE_STATUS_ACTIVE, + Address: servicestypes.GetServiceAddress(1).String(), + Tokens: sdk.NewCoins(), + DelegatorShares: sdk.NewDecCoins(sdk.NewDecCoinFromDec("umilk", sdkmath.LegacyNewDec(100))), + }) + }, + serviceID: 1, + amount: sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100))), + delegator: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + shouldErr: true, + }, + { + name: "invalid delegator address returns error", + store: func(ctx sdk.Context) { + suite.sk.SaveService(ctx, servicestypes.Service{ + ID: 1, + Address: servicestypes.GetServiceAddress(1).String(), + }) + }, + serviceID: 1, + amount: sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100))), + delegator: "invalid", + shouldErr: true, + }, + { + name: "insufficient funds return error", + store: func(ctx sdk.Context) { + // Create the service + suite.sk.SaveService(ctx, servicestypes.Service{ + ID: 1, + Address: servicestypes.GetServiceAddress(1).String(), + }) + + // Set the next service id + suite.sk.SetNextServiceID(ctx, 2) + + // Send some funds to the user + suite.fundAccount( + ctx, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(50))), + ) + }, + serviceID: 1, + amount: sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100))), + delegator: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + shouldErr: true, + }, + { + name: "delegating to an existing service works properly", + store: func(ctx sdk.Context) { + // Create the service + suite.sk.SaveService(ctx, servicestypes.Service{ + ID: 1, + Status: servicestypes.SERVICE_STATUS_ACTIVE, + Address: servicestypes.GetServiceAddress(1).String(), + Tokens: sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(20)), + ), + DelegatorShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("service/1/umilk", sdkmath.LegacyNewDec(100)), + ), + }) + + // Set the correct service tokens amount + suite.fundAccount( + ctx, + servicestypes.GetServiceAddress(1).String(), + sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(20))), + ) + + // Set the next service id + suite.sk.SetNextServiceID(ctx, 2) + + // Send some funds to the user + suite.fundAccount( + ctx, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100))), + ) + }, + serviceID: 1, + amount: sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100))), + delegator: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + shouldErr: false, + expShares: sdk.NewDecCoins(sdk.NewDecCoinFromDec("service/1/umilk", sdkmath.LegacyNewDec(500))), + check: func(ctx sdk.Context) { + // Make sure the service now exists + service, found := suite.sk.GetService(ctx, 1) + suite.Require().True(found) + suite.Require().Equal(servicestypes.Service{ + ID: 1, + Status: servicestypes.SERVICE_STATUS_ACTIVE, + Address: servicestypes.GetServiceAddress(1).String(), + Tokens: sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(120)), + ), + DelegatorShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("service/1/umilk", sdkmath.LegacyNewDec(600)), + ), + }, service) + + // Make sure the delegation exists + delegation, found := suite.k.GetServiceDelegation(ctx, 1, "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4") + suite.Require().True(found) + suite.Require().Equal(types.NewServiceDelegation( + 1, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewDecCoins( + sdk.NewDecCoinFromDec("service/1/umilk", sdkmath.LegacyNewDec(500)), + ), + ), delegation) + + // Make sure the user balance has been reduced properly + userBalance := suite.bk.GetBalance(ctx, sdk.AccAddress("cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4"), "umilk") + suite.Require().Equal(sdk.NewCoin("umilk", sdkmath.NewInt(0)), userBalance) + + // Make sure the service account balance has increased properly + serviceBalance := suite.bk.GetBalance(ctx, servicestypes.GetServiceAddress(1), "umilk") + suite.Require().Equal(sdk.NewCoin("umilk", sdkmath.NewInt(120)), serviceBalance) + }, + }, + { + name: "delegating another token denom works properly", + store: func(ctx sdk.Context) { + // Create the service + suite.sk.SaveService(ctx, servicestypes.Service{ + ID: 1, + Status: servicestypes.SERVICE_STATUS_ACTIVE, + Address: servicestypes.GetServiceAddress(1).String(), + Tokens: sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(80)), + ), + DelegatorShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("service/1/umilk", sdkmath.LegacyNewDec(125)), + ), + }) + + // Set the correct service tokens amount + suite.fundAccount( + ctx, + servicestypes.GetServiceAddress(1).String(), + sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(80))), + ) + + // Set the next service id + suite.sk.SetNextServiceID(ctx, 2) + + // Save the existing delegation + suite.k.SaveServiceDelegation(ctx, types.NewServiceDelegation( + 1, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewDecCoins( + sdk.NewDecCoinFromDec("service/1/umilk", sdkmath.LegacyNewDec(125)), + ), + )) + + // Send some funds to the user + suite.fundAccount( + ctx, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewCoins(sdk.NewCoin("uinit", sdkmath.NewInt(100))), + ) + }, + serviceID: 1, + amount: sdk.NewCoins(sdk.NewCoin("uinit", sdkmath.NewInt(100))), + delegator: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + shouldErr: false, + expShares: sdk.NewDecCoins(sdk.NewDecCoinFromDec("service/1/uinit", sdkmath.LegacyNewDec(100))), + check: func(ctx sdk.Context) { + // Make sure the service now exists + service, found := suite.sk.GetService(ctx, 1) + suite.Require().True(found) + suite.Require().Equal(servicestypes.Service{ + ID: 1, + Status: servicestypes.SERVICE_STATUS_ACTIVE, + Address: servicestypes.GetServiceAddress(1).String(), + Tokens: sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(80)), + sdk.NewCoin("uinit", sdkmath.NewInt(100)), + ), + DelegatorShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("service/1/umilk", sdkmath.LegacyNewDec(125)), + sdk.NewDecCoinFromDec("service/1/uinit", sdkmath.LegacyNewDec(100)), + ), + }, service) + + // Make sure the delegation has been updated properly + delegation, found := suite.k.GetServiceDelegation(ctx, 1, "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4") + suite.Require().True(found) + suite.Require().Equal(types.NewServiceDelegation( + 1, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewDecCoins( + sdk.NewDecCoinFromDec("service/1/umilk", sdkmath.LegacyNewDec(125)), + sdk.NewDecCoinFromDec("service/1/uinit", sdkmath.LegacyNewDec(100)), + ), + ), delegation) + + // Make sure the user balance has been reduced properly + userBalance := suite.bk.GetBalance(ctx, sdk.AccAddress("cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4"), "umilk") + suite.Require().Equal(sdk.NewCoin("umilk", sdkmath.NewInt(0)), userBalance) + + // Make sure the service account balance has increased properly + serviceBalance := suite.bk.GetAllBalances(ctx, servicestypes.GetServiceAddress(1)) + suite.Require().Equal(sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(80)), + sdk.NewCoin("uinit", sdkmath.NewInt(100)), + ), serviceBalance) + }, + }, + { + name: "delegating more tokens works properly", + store: func(ctx sdk.Context) { + // Create the service + suite.sk.SaveService(ctx, servicestypes.Service{ + ID: 1, + Status: servicestypes.SERVICE_STATUS_ACTIVE, + Address: servicestypes.GetServiceAddress(1).String(), + Tokens: sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(80)), + sdk.NewCoin("uinit", sdkmath.NewInt(75)), + ), + DelegatorShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("service/1/umilk", sdkmath.LegacyNewDec(125)), + sdk.NewDecCoinFromDec("service/1/uinit", sdkmath.LegacyNewDec(200)), + ), + }) + + // Set the correct service tokens amount + suite.fundAccount( + ctx, + servicestypes.GetServiceAddress(1).String(), + sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(80)), + sdk.NewCoin("uinit", sdkmath.NewInt(75)), + ), + ) + + // Set the next service id + suite.sk.SetNextServiceID(ctx, 2) + + // Save the existing delegation + suite.k.SaveServiceDelegation(ctx, types.NewServiceDelegation( + 1, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewDecCoins( + sdk.NewDecCoinFromDec("service/1/umilk", sdkmath.LegacyNewDec(100)), + sdk.NewDecCoinFromDec("service/1/uinit", sdkmath.LegacyNewDec(60)), + ), + )) + + // Send some funds to the user + suite.fundAccount( + ctx, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(100)), + sdk.NewCoin("uinit", sdkmath.NewInt(225)), + ), + ) + }, + serviceID: 1, + amount: sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(100)), + sdk.NewCoin("uinit", sdkmath.NewInt(225)), + ), + delegator: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + shouldErr: false, + expShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("service/1/umilk", sdkmath.LegacyNewDecWithPrec(15625, 2)), + sdk.NewDecCoinFromDec("service/1/uinit", sdkmath.LegacyNewDec(600)), + ), + check: func(ctx sdk.Context) { + // Make sure the service now exists + service, found := suite.sk.GetService(ctx, 1) + suite.Require().True(found) + suite.Require().Equal(servicestypes.Service{ + ID: 1, + Status: servicestypes.SERVICE_STATUS_ACTIVE, + Address: servicestypes.GetServiceAddress(1).String(), + Tokens: sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(180)), + sdk.NewCoin("uinit", sdkmath.NewInt(300)), + ), + DelegatorShares: sdk.NewDecCoins( + sdk.NewDecCoinFromDec("service/1/umilk", sdkmath.LegacyNewDecWithPrec(28125, 2)), + sdk.NewDecCoinFromDec("service/1/uinit", sdkmath.LegacyNewDec(800)), + ), + }, service) + + // Make sure the delegation has been updated properly + delegation, found := suite.k.GetServiceDelegation(ctx, 1, "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4") + suite.Require().True(found) + suite.Require().Equal(types.NewServiceDelegation( + 1, + "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4", + sdk.NewDecCoins( + sdk.NewDecCoinFromDec("service/1/umilk", sdkmath.LegacyNewDecWithPrec(25625, 2)), // 100 (existing) + 156.25 (new) + sdk.NewDecCoinFromDec("service/1/uinit", sdkmath.LegacyNewDec(660)), // 60 (existing) + 600 (new) + ), + ), delegation) + + // Make sure the user balance has been reduced properly + userBalance := suite.bk.GetBalance(ctx, sdk.AccAddress("cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4"), "umilk") + suite.Require().Equal(sdk.NewCoin("umilk", sdkmath.NewInt(0)), userBalance) + + // Make sure the service account balance has increased properly + serviceBalance := suite.bk.GetAllBalances(ctx, servicestypes.GetServiceAddress(1)) + suite.Require().Equal(sdk.NewCoins( + sdk.NewCoin("umilk", sdkmath.NewInt(180)), + sdk.NewCoin("uinit", sdkmath.NewInt(300)), + ), serviceBalance) + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.setup != nil { + tc.setup() + } + if tc.store != nil { + tc.store(ctx) + } + + shares, err := suite.k.DelegateToService(ctx, tc.serviceID, tc.amount, tc.delegator) + if tc.shouldErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + suite.Require().Equal(tc.expShares, shares) + } + + if tc.check != nil { + tc.check(ctx) + } + }) + } +} diff --git a/x/restaking/types/codec.go b/x/restaking/types/codec.go new file mode 100644 index 000000000..0c0edf3af --- /dev/null +++ b/x/restaking/types/codec.go @@ -0,0 +1,44 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/legacy" + "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" +) + +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + legacy.RegisterAminoMsg(cdc, &MsgDelegateService{}, "milkyway/MsgDelegateService") + legacy.RegisterAminoMsg(cdc, &MsgDelegatePool{}, "milkyway/MsgDelegatePool") + legacy.RegisterAminoMsg(cdc, &MsgDelegateOperator{}, "milkyway/MsgDelegateOperator") + legacy.RegisterAminoMsg(cdc, &MsgUpdateParams{}, "milkyway/restaking/MsgUpdateParams") +} + +func RegisterInterfaces(registry types.InterfaceRegistry) { + registry.RegisterImplementations((*sdk.Msg)(nil), + &MsgDelegatePool{}, + &MsgDelegateService{}, + &MsgDelegateOperator{}, + &MsgUpdateParams{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var ( + // AminoCdc references the global x/services module codec. Note, the codec should + // ONLY be used in certain instances of tests and for JSON encoding as Amino is + // still used for that purpose. + // + // The actual codec used for serialization should be provided to x/services and + // defined at the application level. + AminoCdc = codec.NewLegacyAmino() +) + +func init() { + RegisterLegacyAminoCodec(AminoCdc) + cryptocodec.RegisterCrypto(AminoCdc) + sdk.RegisterLegacyAminoCodec(AminoCdc) +} diff --git a/x/restaking/types/delegation.go b/x/restaking/types/delegation.go new file mode 100644 index 000000000..d7f2e9c92 --- /dev/null +++ b/x/restaking/types/delegation.go @@ -0,0 +1,44 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// DelegationReceiver is an interface that represents the receiver of a delegation (operator, pool, service, etc). +type DelegationReceiver interface { + GetID() uint32 + GetAddress() string + InvalidExRate() bool +} + +// DelegationGetter represents a function that allows to retrieve an existing delegation +type DelegationGetter func(ctx sdk.Context, receiverID uint32, delegator string) (Delegation, bool) + +// DelegationBuilder represents a function that allows to build a new delegation +type DelegationBuilder func(receiverID uint32, delegator string) Delegation + +// DelegationUpdater represents a function that allows to update an existing delegation +type DelegationUpdater func(ctx sdk.Context, delegation Delegation) (newShares sdk.DecCoins, err error) + +// Delegation is an interface that represents a delegation object. +type Delegation interface { + isDelegation() +} + +// DelegationHooks contains the hooks that can be called before and after a delegation is modified. +type DelegationHooks struct { + BeforeDelegationSharesModified func(ctx sdk.Context, receiverID uint32, delegator string) error + BeforeDelegationCreated func(ctx sdk.Context, receiverID uint32, delegator string) error + AfterDelegationModified func(ctx sdk.Context, receiverID uint32, delegator string) error +} + +// DelegationData contains the data required to perform a delegation. +type DelegationData struct { + Amount sdk.Coins + Delegator string + Receiver DelegationReceiver + GetDelegation DelegationGetter + BuildDelegation DelegationBuilder + UpdateDelegation DelegationUpdater + Hooks DelegationHooks +} diff --git a/x/restaking/types/errors.go b/x/restaking/types/errors.go new file mode 100644 index 000000000..afc438c8e --- /dev/null +++ b/x/restaking/types/errors.go @@ -0,0 +1,11 @@ +package types + +import ( + "cosmossdk.io/errors" +) + +var ( + ErrInvalidGenesis = errors.Register(ModuleName, 1, "invalid genesis state") + ErrInvalidShares = errors.Register(ModuleName, 2, "invalid shares amount") + ErrDelegatorShareExRateInvalid = errors.Register(ModuleName, 3, "cannot delegate to pool/operator/service with invalid (zero) ex-rate") +) diff --git a/x/restaking/types/events.go b/x/restaking/types/events.go new file mode 100644 index 000000000..c3917311a --- /dev/null +++ b/x/restaking/types/events.go @@ -0,0 +1,12 @@ +package types + +const ( + EventTypeDelegatePool = "delegate_pool" + EventTypeDelegateOperator = "delegate_operator" + EventTypeDelegateService = "delegate_service" + + AttributeKeyDelegator = "delegator" + AttributeKeyOperatorID = "operator_id" + AttributeKeyServiceID = "service_id" + AttributeKeyNewShares = "new_shares" +) diff --git a/x/restaking/types/expected_keepers.go b/x/restaking/types/expected_keepers.go new file mode 100644 index 000000000..e0505c813 --- /dev/null +++ b/x/restaking/types/expected_keepers.go @@ -0,0 +1,35 @@ +package types + +import ( + "context" + + "cosmossdk.io/core/address" + sdk "github.com/cosmos/cosmos-sdk/types" + + operatorstypes "github.com/milkyway-labs/milkyway/x/operators/types" + poolstypes "github.com/milkyway-labs/milkyway/x/pools/types" + servicestypes "github.com/milkyway-labs/milkyway/x/services/types" +) + +type AccountKeeper interface { + AddressCodec() address.Codec +} + +type BankKeeper interface { + SendCoins(ctx context.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) error +} + +type PoolsKeeper interface { + CreateOrGetPoolByDenom(ctx sdk.Context, denom string) (poolstypes.Pool, error) + SavePool(ctx sdk.Context, pool poolstypes.Pool) error +} + +type OperatorsKeeper interface { + GetOperator(ctx sdk.Context, operatorID uint32) (operatorstypes.Operator, bool) + SaveOperator(ctx sdk.Context, operator operatorstypes.Operator) +} + +type ServicesKeeper interface { + GetService(ctx sdk.Context, serviceID uint32) (servicestypes.Service, bool) + SaveService(ctx sdk.Context, service servicestypes.Service) +} diff --git a/x/restaking/types/genesis.go b/x/restaking/types/genesis.go new file mode 100644 index 000000000..121b6d7b9 --- /dev/null +++ b/x/restaking/types/genesis.go @@ -0,0 +1,60 @@ +package types + +import ( + "fmt" +) + +// NewGenesis creates a new genesis state +func NewGenesis( + poolsDelegations []PoolDelegation, + servicesDelegations []ServiceDelegation, + operatorsDelegations []OperatorDelegation, + params Params, +) *GenesisState { + return &GenesisState{ + PoolsDelegations: poolsDelegations, + ServicesDelegations: servicesDelegations, + OperatorsDelegations: operatorsDelegations, + Params: params, + } +} + +// DefaultGenesis returns a default genesis state +func DefaultGenesis() *GenesisState { + return NewGenesis(nil, nil, nil, DefaultParams()) +} + +// Validate performs basic validation of genesis data +func (g *GenesisState) Validate() error { + // Validate pools delegations + for _, entry := range g.PoolsDelegations { + err := entry.Validate() + if err != nil { + return fmt.Errorf("invalid pool delegation: %w", err) + } + } + + // Validate services delegations + for _, entry := range g.ServicesDelegations { + err := entry.Validate() + if err != nil { + return fmt.Errorf("invalid service delegation: %w", err) + } + } + + // Validate operators delegations + for _, entry := range g.OperatorsDelegations { + err := entry.Validate() + if err != nil { + return fmt.Errorf("invalid operator delegation: %w", err) + } + } + + // Validate the params + err := g.Params.Validate() + if err != nil { + return fmt.Errorf("invalid params: %w", err) + } + + return nil +} diff --git a/x/restaking/types/genesis.pb.go b/x/restaking/types/genesis.pb.go new file mode 100644 index 000000000..05dc74b36 --- /dev/null +++ b/x/restaking/types/genesis.pb.go @@ -0,0 +1,522 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: milkyway/restaking/v1/genesis.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// GenesisState defines the restaking module's genesis state. +type GenesisState struct { + // Params defines the parameters of the module. + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` + // PoolDelegations represents the delegations to pools. + PoolsDelegations []PoolDelegation `protobuf:"bytes,2,rep,name=pools_delegations,json=poolsDelegations,proto3" json:"pools_delegations"` + // ServiceDelegations represents the delegations to services. + ServicesDelegations []ServiceDelegation `protobuf:"bytes,3,rep,name=services_delegations,json=servicesDelegations,proto3" json:"services_delegations"` + // OperatorDelegations represents the delegations to operators. + OperatorsDelegations []OperatorDelegation `protobuf:"bytes,4,rep,name=operators_delegations,json=operatorsDelegations,proto3" json:"operators_delegations"` +} + +func (m *GenesisState) Reset() { *m = GenesisState{} } +func (m *GenesisState) String() string { return proto.CompactTextString(m) } +func (*GenesisState) ProtoMessage() {} +func (*GenesisState) Descriptor() ([]byte, []int) { + return fileDescriptor_0378bd63cae7d256, []int{0} +} +func (m *GenesisState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenesisState.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GenesisState) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenesisState.Merge(m, src) +} +func (m *GenesisState) XXX_Size() int { + return m.Size() +} +func (m *GenesisState) XXX_DiscardUnknown() { + xxx_messageInfo_GenesisState.DiscardUnknown(m) +} + +var xxx_messageInfo_GenesisState proto.InternalMessageInfo + +func (m *GenesisState) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +func (m *GenesisState) GetPoolsDelegations() []PoolDelegation { + if m != nil { + return m.PoolsDelegations + } + return nil +} + +func (m *GenesisState) GetServicesDelegations() []ServiceDelegation { + if m != nil { + return m.ServicesDelegations + } + return nil +} + +func (m *GenesisState) GetOperatorsDelegations() []OperatorDelegation { + if m != nil { + return m.OperatorsDelegations + } + return nil +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "milkyway.restaking.v1.GenesisState") +} + +func init() { + proto.RegisterFile("milkyway/restaking/v1/genesis.proto", fileDescriptor_0378bd63cae7d256) +} + +var fileDescriptor_0378bd63cae7d256 = []byte{ + // 336 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x91, 0xbf, 0x4e, 0xfa, 0x50, + 0x14, 0x80, 0x5b, 0x20, 0x0c, 0xe5, 0x37, 0xfc, 0xac, 0x90, 0x10, 0x12, 0xaf, 0x04, 0x63, 0x82, + 0x83, 0xbd, 0x29, 0x8c, 0x6e, 0xc4, 0xc4, 0xc9, 0x68, 0x64, 0x31, 0x2e, 0xe6, 0xb6, 0x9c, 0xd4, + 0x1b, 0xda, 0x9e, 0xa6, 0xe7, 0x5a, 0xe5, 0x2d, 0x7c, 0x2c, 0x46, 0x46, 0x27, 0x63, 0x60, 0xf7, + 0x19, 0x8c, 0x6d, 0xa9, 0x10, 0xa8, 0x5b, 0xff, 0x7c, 0xe7, 0xfb, 0x4e, 0x72, 0x8c, 0x93, 0x40, + 0xfa, 0xd3, 0xd9, 0x8b, 0x98, 0xf1, 0x18, 0x48, 0x89, 0xa9, 0x0c, 0x3d, 0x9e, 0xd8, 0xdc, 0x83, + 0x10, 0x48, 0x92, 0x15, 0xc5, 0xa8, 0xd0, 0x6c, 0xad, 0x21, 0xab, 0x80, 0xac, 0xc4, 0xee, 0x34, + 0x3d, 0xf4, 0x30, 0x25, 0xf8, 0xcf, 0x53, 0x06, 0x77, 0x98, 0x8b, 0x14, 0x20, 0x71, 0x47, 0x10, + 0xf0, 0xc4, 0x76, 0x40, 0x09, 0x9b, 0xbb, 0x28, 0xc3, 0xfc, 0x7f, 0x6f, 0x7f, 0x31, 0xc0, 0x09, + 0xf8, 0xf4, 0x37, 0x13, 0x89, 0x58, 0x04, 0x39, 0xd3, 0xfb, 0xaa, 0x18, 0xff, 0xae, 0xb2, 0x35, + 0xc7, 0x4a, 0x28, 0x30, 0x2f, 0x8c, 0x7a, 0x06, 0xb4, 0xf5, 0xae, 0xde, 0x6f, 0x0c, 0x8e, 0xac, + 0xbd, 0x6b, 0x5b, 0xb7, 0x29, 0x34, 0xaa, 0xcd, 0x3f, 0x8e, 0xb5, 0xbb, 0x7c, 0xc4, 0xbc, 0x37, + 0x0e, 0x22, 0x44, 0x9f, 0x1e, 0x27, 0xe0, 0x83, 0x27, 0x94, 0xc4, 0x90, 0xda, 0x95, 0x6e, 0xb5, + 0xdf, 0x18, 0x9c, 0x96, 0x79, 0x10, 0xfd, 0xcb, 0x82, 0xce, 0x7d, 0xff, 0x53, 0xcb, 0xef, 0x67, + 0x32, 0x85, 0xd1, 0x24, 0x88, 0x13, 0xe9, 0xc2, 0xb6, 0xbc, 0x9a, 0xca, 0xfb, 0x25, 0xf2, 0x71, + 0x36, 0xb2, 0xe3, 0x3f, 0x5c, 0xbb, 0x36, 0x13, 0x13, 0xa3, 0x85, 0x11, 0xc4, 0x42, 0x61, 0xbc, + 0xdd, 0xa8, 0xa5, 0x8d, 0xb3, 0x92, 0xc6, 0x4d, 0x3e, 0xb3, 0x13, 0x69, 0x16, 0xb6, 0x8d, 0xca, + 0xe8, 0x7a, 0xbe, 0x64, 0xfa, 0x62, 0xc9, 0xf4, 0xcf, 0x25, 0xd3, 0xdf, 0x56, 0x4c, 0x5b, 0xac, + 0x98, 0xf6, 0xbe, 0x62, 0xda, 0xc3, 0xd0, 0x93, 0xea, 0xe9, 0xd9, 0xb1, 0x5c, 0x0c, 0xf8, 0x3a, + 0x75, 0xee, 0x0b, 0x87, 0x8a, 0x37, 0xfe, 0xba, 0x71, 0x49, 0x35, 0x8b, 0x80, 0x9c, 0x7a, 0x7a, + 0xc6, 0xe1, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x35, 0xb2, 0x3d, 0x77, 0x82, 0x02, 0x00, 0x00, +} + +func (m *GenesisState) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GenesisState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.OperatorsDelegations) > 0 { + for iNdEx := len(m.OperatorsDelegations) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.OperatorsDelegations[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if len(m.ServicesDelegations) > 0 { + for iNdEx := len(m.ServicesDelegations) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ServicesDelegations[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.PoolsDelegations) > 0 { + for iNdEx := len(m.PoolsDelegations) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.PoolsDelegations[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int { + offset -= sovGenesis(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *GenesisState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovGenesis(uint64(l)) + if len(m.PoolsDelegations) > 0 { + for _, e := range m.PoolsDelegations { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.ServicesDelegations) > 0 { + for _, e := range m.ServicesDelegations { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.OperatorsDelegations) > 0 { + for _, e := range m.OperatorsDelegations { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + return n +} + +func sovGenesis(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozGenesis(x uint64) (n int) { + return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *GenesisState) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GenesisState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PoolsDelegations", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PoolsDelegations = append(m.PoolsDelegations, PoolDelegation{}) + if err := m.PoolsDelegations[len(m.PoolsDelegations)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ServicesDelegations", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ServicesDelegations = append(m.ServicesDelegations, ServiceDelegation{}) + if err := m.ServicesDelegations[len(m.ServicesDelegations)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OperatorsDelegations", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.OperatorsDelegations = append(m.OperatorsDelegations, OperatorDelegation{}) + if err := m.OperatorsDelegations[len(m.OperatorsDelegations)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipGenesis(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthGenesis + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupGenesis + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthGenesis + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthGenesis = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGenesis = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/restaking/types/genesis_test.go b/x/restaking/types/genesis_test.go new file mode 100644 index 000000000..ba2a600ce --- /dev/null +++ b/x/restaking/types/genesis_test.go @@ -0,0 +1,123 @@ +package types_test + +import ( + "testing" + "time" + + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + + "github.com/milkyway-labs/milkyway/x/restaking/types" +) + +func TestGenesis_Validate(t *testing.T) { + testCases := []struct { + name string + genesis *types.GenesisState + shouldErr bool + }{ + { + name: "invalid pool delegation entry returns error", + genesis: types.NewGenesis( + []types.PoolDelegation{ + types.NewPoolDelegation( + 0, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdkmath.LegacyNewDec(100), + ), + }, + nil, + nil, + types.DefaultParams(), + ), + shouldErr: true, + }, + { + name: "invalid service delegation entry returns error", + genesis: types.NewGenesis( + nil, + []types.ServiceDelegation{ + types.NewServiceDelegation( + 0, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdk.NewDecCoins(sdk.NewDecCoinFromDec("umilk", sdkmath.LegacyNewDec(100))), + ), + }, + nil, + types.DefaultParams(), + ), + shouldErr: true, + }, + { + name: "invalid operator delegation entry returns error", + genesis: types.NewGenesis( + nil, + nil, + []types.OperatorDelegation{ + types.NewOperatorDelegation( + 0, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdk.NewDecCoins(sdk.NewDecCoinFromDec("umilk", sdkmath.LegacyNewDec(100))), + ), + }, + types.DefaultParams(), + ), + shouldErr: true, + }, + { + name: "invalid params return error", + genesis: types.NewGenesis( + nil, + nil, + nil, + types.NewParams(0), + ), + shouldErr: true, + }, + { + name: "default genesis returns no error", + genesis: types.DefaultGenesis(), + shouldErr: false, + }, + { + name: "valid genesis returns no error", + genesis: types.NewGenesis( + []types.PoolDelegation{ + types.NewPoolDelegation( + 1, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdkmath.LegacyNewDec(100), + ), + }, + []types.ServiceDelegation{ + types.NewServiceDelegation( + 2, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdk.NewDecCoins(sdk.NewDecCoinFromDec("umilk", sdkmath.LegacyNewDec(100))), + ), + }, + []types.OperatorDelegation{ + types.NewOperatorDelegation( + 3, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdk.NewDecCoins(sdk.NewDecCoinFromDec("umilk", sdkmath.LegacyNewDec(100))), + ), + }, + types.NewParams(5*24*time.Hour), + ), + shouldErr: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := tc.genesis.Validate() + if tc.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/x/restaking/types/hooks.go b/x/restaking/types/hooks.go new file mode 100644 index 000000000..e13cda079 --- /dev/null +++ b/x/restaking/types/hooks.go @@ -0,0 +1,29 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type RestakingHooks interface { + PoolRestakingHooks + OperatorRestakingHooks + ServiceRestakingHooks +} + +type PoolRestakingHooks interface { + BeforePoolDelegationCreated(ctx sdk.Context, poolID uint32, delegator string) error + BeforePoolDelegationSharesModified(ctx sdk.Context, poolID uint32, delegator string) error + AfterPoolDelegationModified(ctx sdk.Context, poolID uint32, delegator string) error +} + +type OperatorRestakingHooks interface { + BeforeOperatorDelegationCreated(ctx sdk.Context, operatorID uint32, delegator string) error + BeforeOperatorDelegationSharesModified(ctx sdk.Context, operatorID uint32, delegator string) error + AfterOperatorDelegationModified(ctx sdk.Context, operatorID uint32, delegator string) error +} + +type ServiceRestakingHooks interface { + BeforeServiceDelegationCreated(ctx sdk.Context, serviceID uint32, delegator string) error + BeforeServiceDelegationSharesModified(ctx sdk.Context, serviceID uint32, delegator string) error + AfterServiceDelegationModified(ctx sdk.Context, serviceID uint32, delegator string) error +} diff --git a/x/restaking/types/keys.go b/x/restaking/types/keys.go new file mode 100644 index 000000000..226b644b6 --- /dev/null +++ b/x/restaking/types/keys.go @@ -0,0 +1,153 @@ +package types + +import ( + "bytes" + "fmt" + + operatorstypes "github.com/milkyway-labs/milkyway/x/operators/types" + poolstypes "github.com/milkyway-labs/milkyway/x/pools/types" + servicestypes "github.com/milkyway-labs/milkyway/x/services/types" +) + +const ( + ModuleName = "restaking" + StoreKey = ModuleName +) + +var ( + ParamsKey = []byte{0x01} + + PoolDelegationPrefix = []byte{0xa1} + UnbondingPoolDelegationPrefix = []byte{0xa2} + PoolDelegationsByPoolIDPrefix = []byte{0x71} + + ServiceDelegationPrefix = []byte{0xb1} + UnbondingServiceDelegationPrefix = []byte{0xb2} + + OperatorDelegationPrefix = []byte{0xc1} + UnbondingOperatorDelegationPrefix = []byte{0xc2} +) + +// UserPoolDelegationsStorePrefix returns the prefix used to store all the delegations to a given pool +func UserPoolDelegationsStorePrefix(userAddress string) []byte { + return append(PoolDelegationPrefix, []byte(userAddress)...) +} + +// UserPoolDelegationStoreKey returns the key used to store the user -> pool delegation association +func UserPoolDelegationStoreKey(delegator string, poolID uint32) []byte { + return append(UserPoolDelegationsStorePrefix(delegator), poolstypes.GetPoolIDBytes(poolID)...) +} + +// DelegationsByPoolIDStorePrefix returns the prefix used to store the delegations to a given pool +func DelegationsByPoolIDStorePrefix(poolID uint32) []byte { + return append(PoolDelegationsByPoolIDPrefix, poolstypes.GetPoolIDBytes(poolID)...) +} + +// DelegationByPoolIDStoreKey returns the key used to store the pool -> user delegation association +func DelegationByPoolIDStoreKey(poolID uint32, delegatorAddress string) []byte { + return append(DelegationsByPoolIDStorePrefix(poolID), []byte(delegatorAddress)...) +} + +// ParseDelegationsByPoolIDKey parses the pool ID and delegator address from the given key +func ParseDelegationsByPoolIDKey(bz []byte) (poolID uint32, delegatorAddress string, err error) { + prefixLength := len(PoolDelegationsByPoolIDPrefix) + if prefix := bz[:prefixLength]; !bytes.Equal(prefix, PoolDelegationsByPoolIDPrefix) { + return 0, "", fmt.Errorf("invalid prefix; expected: %X, got: %x", PoolDelegationsByPoolIDPrefix, prefix) + } + + // Remove the prefix + bz = bz[prefixLength:] + + // Read the pool ID + poolID = poolstypes.GetPoolIDFromBytes(bz[:4]) + bz = bz[4:] + + // Read the delegator address + delegatorAddress = string(bz) + + return poolID, delegatorAddress, nil +} + +// -------------------------------------------------------------------------------------------------------------------- + +// UserOperatorDelegationsStorePrefix returns the prefix used to store all the delegations to a given operator +func UserOperatorDelegationsStorePrefix(userAddress string) []byte { + return append(OperatorDelegationPrefix, []byte(userAddress)...) +} + +// UserOperatorDelegationStoreKey returns the key used to store the user -> operator delegation association +func UserOperatorDelegationStoreKey(delegator string, operatorID uint32) []byte { + return append(UserOperatorDelegationsStorePrefix(delegator), operatorstypes.GetOperatorIDBytes(operatorID)...) +} + +// DelegationsByOperatorIDStorePrefix returns the prefix used to store the delegations to a given operator +func DelegationsByOperatorIDStorePrefix(operatorID uint32) []byte { + return append(OperatorDelegationPrefix, operatorstypes.GetOperatorIDBytes(operatorID)...) +} + +// DelegationByOperatorIDStoreKey returns the key used to store the operator -> user delegation association +func DelegationByOperatorIDStoreKey(operatorID uint32, delegatorAddress string) []byte { + return append(DelegationsByOperatorIDStorePrefix(operatorID), []byte(delegatorAddress)...) +} + +// ParseDelegationsByOperatorIDKey parses the operator ID and delegator address from the given key +func ParseDelegationsByOperatorIDKey(bz []byte) (operatorID uint32, delegatorAddress string, err error) { + prefixLength := len(OperatorDelegationPrefix) + if prefix := bz[:prefixLength]; !bytes.Equal(prefix, OperatorDelegationPrefix) { + return 0, "", fmt.Errorf("invalid prefix; expected: %X, got: %x", OperatorDelegationPrefix, prefix) + } + + // Remove the prefix + bz = bz[prefixLength:] + + // Read the operator ID + operatorID = operatorstypes.GetOperatorIDFromBytes(bz[:4]) + bz = bz[4:] + + // Read the delegator address + delegatorAddress = string(bz) + + return operatorID, delegatorAddress, nil +} + +// -------------------------------------------------------------------------------------------------------------------- + +// UserServiceDelegationsStorePrefix returns the prefix used to store all the delegations to a given service +func UserServiceDelegationsStorePrefix(userAddress string) []byte { + return append(ServiceDelegationPrefix, []byte(userAddress)...) +} + +// UserServiceDelegationStoreKey returns the key used to store the user -> service delegation association +func UserServiceDelegationStoreKey(delegator string, serviceID uint32) []byte { + return append(UserServiceDelegationsStorePrefix(delegator), servicestypes.GetServiceIDBytes(serviceID)...) +} + +// DelegationsByServiceIDStorePrefix returns the prefix used to store the delegations to a given service +func DelegationsByServiceIDStorePrefix(serviceID uint32) []byte { + return append(ServiceDelegationPrefix, servicestypes.GetServiceIDBytes(serviceID)...) +} + +// DelegationByServiceIDStoreKey returns the key used to store the service -> user delegation association +func DelegationByServiceIDStoreKey(serviceID uint32, delegatorAddress string) []byte { + return append(DelegationsByServiceIDStorePrefix(serviceID), []byte(delegatorAddress)...) +} + +// ParseDelegationsByServiceIDKey parses the service ID and delegator address from the given key +func ParseDelegationsByServiceIDKey(bz []byte) (serviceID uint32, delegatorAddress string, err error) { + prefixLength := len(ServiceDelegationPrefix) + if prefix := bz[:prefixLength]; !bytes.Equal(prefix, ServiceDelegationPrefix) { + return 0, "", fmt.Errorf("invalid prefix; expected: %X, got: %x", ServiceDelegationPrefix, prefix) + } + + // Remove the prefix + bz = bz[prefixLength:] + + // Read the service ID + serviceID = servicestypes.GetServiceIDFromBytes(bz[:4]) + bz = bz[4:] + + // Read the delegator address + delegatorAddress = string(bz) + + return serviceID, delegatorAddress, nil +} diff --git a/x/restaking/types/messages.go b/x/restaking/types/messages.go new file mode 100644 index 000000000..d91e47f4b --- /dev/null +++ b/x/restaking/types/messages.go @@ -0,0 +1,154 @@ +package types + +import ( + "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// NewMsgDelegatePool creates a new MsgDelegatePool instance +func NewMsgDelegatePool(amount sdk.Coin, delegator string) *MsgDelegatePool { + return &MsgDelegatePool{ + Amount: amount, + Delegator: delegator, + } +} + +// ValidateBasic implements sdk.Msg +func (msg *MsgDelegatePool) ValidateBasic() error { + if !msg.Amount.IsValid() || msg.Amount.IsZero() { + return errors.Wrap(sdkerrors.ErrInvalidRequest, "invalid amount") + } + + _, err := sdk.AccAddressFromBech32(msg.Delegator) + if err != nil { + return errors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid delegator address") + } + + return nil +} + +// GetSignBytes implements sdk.Msg +func (msg *MsgDelegatePool) GetSignBytes() []byte { + return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(msg)) +} + +// GetSigners implements sdk.Msg +func (msg *MsgDelegatePool) GetSigners() []sdk.AccAddress { + addr, _ := sdk.AccAddressFromBech32(msg.Delegator) + return []sdk.AccAddress{addr} +} + +// -------------------------------------------------------------------------------------------------------------------- + +// NewMsgDelegateOperator creates a new MsgDelegateOperator instance +func NewMsgDelegateOperator(operatorID uint32, amount sdk.Coins, delegator string) *MsgDelegateOperator { + return &MsgDelegateOperator{ + OperatorID: operatorID, + Amount: amount, + Delegator: delegator, + } +} + +// ValidateBasic implements sdk.Msg +func (msg *MsgDelegateOperator) ValidateBasic() error { + if msg.OperatorID == 0 { + return errors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid operator id") + } + + if !msg.Amount.IsValid() || msg.Amount.IsZero() { + return errors.Wrap(sdkerrors.ErrInvalidRequest, "invalid amount") + } + + _, err := sdk.AccAddressFromBech32(msg.Delegator) + if err != nil { + return errors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid delegator address") + } + + return nil +} + +// GetSignBytes implements sdk.Msg +func (msg *MsgDelegateOperator) GetSignBytes() []byte { + return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(msg)) +} + +// GetSigners implements sdk.Msg +func (msg *MsgDelegateOperator) GetSigners() []sdk.AccAddress { + addr, _ := sdk.AccAddressFromBech32(msg.Delegator) + return []sdk.AccAddress{addr} +} + +// -------------------------------------------------------------------------------------------------------------------- + +func NewMsgDelegateService(serviceID uint32, amount sdk.Coins, delegator string) *MsgDelegateService { + return &MsgDelegateService{ + ServiceID: serviceID, + Amount: amount, + Delegator: delegator, + } +} + +// ValidateBasic implements sdk.Msg +func (msg *MsgDelegateService) ValidateBasic() error { + if msg.ServiceID == 0 { + return errors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid service id") + } + + if !msg.Amount.IsValid() || msg.Amount.IsZero() { + return errors.Wrap(sdkerrors.ErrInvalidRequest, "invalid amount") + } + + _, err := sdk.AccAddressFromBech32(msg.Delegator) + if err != nil { + return errors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid delegator address") + } + + return nil +} + +// GetSignBytes implements sdk.Msg +func (msg *MsgDelegateService) GetSignBytes() []byte { + return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(msg)) +} + +// GetSigners implements sdk.Msg +func (msg *MsgDelegateService) GetSigners() []sdk.AccAddress { + addr, _ := sdk.AccAddressFromBech32(msg.Delegator) + return []sdk.AccAddress{addr} +} + +// -------------------------------------------------------------------------------------------------------------------- + +func NewMsgUpdateParams(params Params, authority string) *MsgUpdateParams { + return &MsgUpdateParams{ + Params: params, + Authority: authority, + } +} + +// ValidateBasic implements sdk.Msg +func (msg *MsgUpdateParams) ValidateBasic() error { + err := msg.Params.Validate() + if err != nil { + return errors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid params: %s", err) + } + + _, err = sdk.AccAddressFromBech32(msg.Authority) + if err != nil { + return errors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid authority address") + } + + return nil +} + +// GetSignBytes implements sdk.Msg +func (msg *MsgUpdateParams) GetSignBytes() []byte { + return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(msg)) +} + +// GetSigners implements sdk.Msg +func (msg *MsgUpdateParams) GetSigners() []sdk.AccAddress { + addr, _ := sdk.AccAddressFromBech32(msg.Authority) + return []sdk.AccAddress{addr} +} diff --git a/x/restaking/types/messages.pb.go b/x/restaking/types/messages.pb.go new file mode 100644 index 000000000..497aaa721 --- /dev/null +++ b/x/restaking/types/messages.pb.go @@ -0,0 +1,1884 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: milkyway/restaking/v1/messages.proto + +package types + +import ( + context "context" + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/cosmos-sdk/types/msgservice" + _ "github.com/cosmos/cosmos-sdk/types/tx/amino" + _ "github.com/cosmos/cosmos-sdk/x/bank/types" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// MsgDelegatePool defines the message structure for the DelegatePool gRPC +// service method. It allows a user to put their assets into a restaking pool +// that will later be used to provide cryptoeconomic security to services that +// choose it. +type MsgDelegatePool struct { + // Delegator is the address of the user joining the pool + Delegator string `protobuf:"bytes,1,opt,name=delegator,proto3" json:"delegator,omitempty"` + // Amount is the amount of coins to be staked + Amount types.Coin `protobuf:"bytes,2,opt,name=amount,proto3" json:"amount"` +} + +func (m *MsgDelegatePool) Reset() { *m = MsgDelegatePool{} } +func (m *MsgDelegatePool) String() string { return proto.CompactTextString(m) } +func (*MsgDelegatePool) ProtoMessage() {} +func (*MsgDelegatePool) Descriptor() ([]byte, []int) { + return fileDescriptor_9772be2b1a923bdb, []int{0} +} +func (m *MsgDelegatePool) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgDelegatePool) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgDelegatePool.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgDelegatePool) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgDelegatePool.Merge(m, src) +} +func (m *MsgDelegatePool) XXX_Size() int { + return m.Size() +} +func (m *MsgDelegatePool) XXX_DiscardUnknown() { + xxx_messageInfo_MsgDelegatePool.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgDelegatePool proto.InternalMessageInfo + +func (m *MsgDelegatePool) GetDelegator() string { + if m != nil { + return m.Delegator + } + return "" +} + +func (m *MsgDelegatePool) GetAmount() types.Coin { + if m != nil { + return m.Amount + } + return types.Coin{} +} + +// MsgDelegatePoolResponse defines the return value of MsgDelegatePool. +type MsgDelegatePoolResponse struct { +} + +func (m *MsgDelegatePoolResponse) Reset() { *m = MsgDelegatePoolResponse{} } +func (m *MsgDelegatePoolResponse) String() string { return proto.CompactTextString(m) } +func (*MsgDelegatePoolResponse) ProtoMessage() {} +func (*MsgDelegatePoolResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_9772be2b1a923bdb, []int{1} +} +func (m *MsgDelegatePoolResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgDelegatePoolResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgDelegatePoolResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgDelegatePoolResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgDelegatePoolResponse.Merge(m, src) +} +func (m *MsgDelegatePoolResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgDelegatePoolResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgDelegatePoolResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgDelegatePoolResponse proto.InternalMessageInfo + +// MsgDelegateOperator defines the message structure for the OperatorRestake +// gRPC service method. It allows a user to delegate their assets to an +// operator. +type MsgDelegateOperator struct { + // Delegator is the address of the user delegating to the operator + Delegator string `protobuf:"bytes,1,opt,name=delegator,proto3" json:"delegator,omitempty"` + // OperatorID is the ID of the operator to delegate to + OperatorID uint32 `protobuf:"varint,2,opt,name=operator_id,json=operatorId,proto3" json:"operator_id,omitempty"` + // Amount is the amount of coins to be delegated + Amount github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=amount,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount"` +} + +func (m *MsgDelegateOperator) Reset() { *m = MsgDelegateOperator{} } +func (m *MsgDelegateOperator) String() string { return proto.CompactTextString(m) } +func (*MsgDelegateOperator) ProtoMessage() {} +func (*MsgDelegateOperator) Descriptor() ([]byte, []int) { + return fileDescriptor_9772be2b1a923bdb, []int{2} +} +func (m *MsgDelegateOperator) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgDelegateOperator) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgDelegateOperator.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgDelegateOperator) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgDelegateOperator.Merge(m, src) +} +func (m *MsgDelegateOperator) XXX_Size() int { + return m.Size() +} +func (m *MsgDelegateOperator) XXX_DiscardUnknown() { + xxx_messageInfo_MsgDelegateOperator.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgDelegateOperator proto.InternalMessageInfo + +func (m *MsgDelegateOperator) GetDelegator() string { + if m != nil { + return m.Delegator + } + return "" +} + +func (m *MsgDelegateOperator) GetOperatorID() uint32 { + if m != nil { + return m.OperatorID + } + return 0 +} + +func (m *MsgDelegateOperator) GetAmount() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.Amount + } + return nil +} + +// MsgDelegateOperatorResponse is the return value of MsgDelegateOperator. +type MsgDelegateOperatorResponse struct { +} + +func (m *MsgDelegateOperatorResponse) Reset() { *m = MsgDelegateOperatorResponse{} } +func (m *MsgDelegateOperatorResponse) String() string { return proto.CompactTextString(m) } +func (*MsgDelegateOperatorResponse) ProtoMessage() {} +func (*MsgDelegateOperatorResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_9772be2b1a923bdb, []int{3} +} +func (m *MsgDelegateOperatorResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgDelegateOperatorResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgDelegateOperatorResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgDelegateOperatorResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgDelegateOperatorResponse.Merge(m, src) +} +func (m *MsgDelegateOperatorResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgDelegateOperatorResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgDelegateOperatorResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgDelegateOperatorResponse proto.InternalMessageInfo + +// MsgDelegateService defines the message structure for the ServiceRestake gRPC +// service method. It allows a user to delegate their assets to a service. +type MsgDelegateService struct { + // Delegator is the address of the user delegating to the service + Delegator string `protobuf:"bytes,1,opt,name=delegator,proto3" json:"delegator,omitempty"` + // ServiceID is the ID of the service to delegate to + ServiceID uint32 `protobuf:"varint,2,opt,name=service_id,json=serviceId,proto3" json:"service_id,omitempty"` + // Amount is the amount of coins to be delegated + Amount github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=amount,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount"` +} + +func (m *MsgDelegateService) Reset() { *m = MsgDelegateService{} } +func (m *MsgDelegateService) String() string { return proto.CompactTextString(m) } +func (*MsgDelegateService) ProtoMessage() {} +func (*MsgDelegateService) Descriptor() ([]byte, []int) { + return fileDescriptor_9772be2b1a923bdb, []int{4} +} +func (m *MsgDelegateService) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgDelegateService) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgDelegateService.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgDelegateService) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgDelegateService.Merge(m, src) +} +func (m *MsgDelegateService) XXX_Size() int { + return m.Size() +} +func (m *MsgDelegateService) XXX_DiscardUnknown() { + xxx_messageInfo_MsgDelegateService.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgDelegateService proto.InternalMessageInfo + +func (m *MsgDelegateService) GetDelegator() string { + if m != nil { + return m.Delegator + } + return "" +} + +func (m *MsgDelegateService) GetServiceID() uint32 { + if m != nil { + return m.ServiceID + } + return 0 +} + +func (m *MsgDelegateService) GetAmount() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.Amount + } + return nil +} + +// MsgDelegateServiceResponse is the return value of MsgDelegateService. +type MsgDelegateServiceResponse struct { +} + +func (m *MsgDelegateServiceResponse) Reset() { *m = MsgDelegateServiceResponse{} } +func (m *MsgDelegateServiceResponse) String() string { return proto.CompactTextString(m) } +func (*MsgDelegateServiceResponse) ProtoMessage() {} +func (*MsgDelegateServiceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_9772be2b1a923bdb, []int{5} +} +func (m *MsgDelegateServiceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgDelegateServiceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgDelegateServiceResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgDelegateServiceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgDelegateServiceResponse.Merge(m, src) +} +func (m *MsgDelegateServiceResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgDelegateServiceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgDelegateServiceResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgDelegateServiceResponse proto.InternalMessageInfo + +// MsgDeactivateService defines the message structure for the UpdateParams gRPC +// service method. It allows the authority to update the module parameters. +type MsgUpdateParams struct { + // Authority is the address that controls the module (defaults to x/gov unless + // overwritten). + Authority string `protobuf:"bytes,1,opt,name=authority,proto3" json:"authority,omitempty" yaml:"authority"` + // Params define the parameters to update. + // + // NOTE: All parameters must be supplied. + Params Params `protobuf:"bytes,2,opt,name=params,proto3" json:"params"` +} + +func (m *MsgUpdateParams) Reset() { *m = MsgUpdateParams{} } +func (m *MsgUpdateParams) String() string { return proto.CompactTextString(m) } +func (*MsgUpdateParams) ProtoMessage() {} +func (*MsgUpdateParams) Descriptor() ([]byte, []int) { + return fileDescriptor_9772be2b1a923bdb, []int{6} +} +func (m *MsgUpdateParams) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUpdateParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUpdateParams.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgUpdateParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUpdateParams.Merge(m, src) +} +func (m *MsgUpdateParams) XXX_Size() int { + return m.Size() +} +func (m *MsgUpdateParams) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUpdateParams.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUpdateParams proto.InternalMessageInfo + +func (m *MsgUpdateParams) GetAuthority() string { + if m != nil { + return m.Authority + } + return "" +} + +func (m *MsgUpdateParams) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +// MsgDeactivateServiceResponse is the return value of MsgUpdateParams. +type MsgUpdateParamsResponse struct { +} + +func (m *MsgUpdateParamsResponse) Reset() { *m = MsgUpdateParamsResponse{} } +func (m *MsgUpdateParamsResponse) String() string { return proto.CompactTextString(m) } +func (*MsgUpdateParamsResponse) ProtoMessage() {} +func (*MsgUpdateParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_9772be2b1a923bdb, []int{7} +} +func (m *MsgUpdateParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUpdateParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUpdateParamsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgUpdateParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUpdateParamsResponse.Merge(m, src) +} +func (m *MsgUpdateParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgUpdateParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUpdateParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUpdateParamsResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgDelegatePool)(nil), "milkyway.restaking.v1.MsgDelegatePool") + proto.RegisterType((*MsgDelegatePoolResponse)(nil), "milkyway.restaking.v1.MsgDelegatePoolResponse") + proto.RegisterType((*MsgDelegateOperator)(nil), "milkyway.restaking.v1.MsgDelegateOperator") + proto.RegisterType((*MsgDelegateOperatorResponse)(nil), "milkyway.restaking.v1.MsgDelegateOperatorResponse") + proto.RegisterType((*MsgDelegateService)(nil), "milkyway.restaking.v1.MsgDelegateService") + proto.RegisterType((*MsgDelegateServiceResponse)(nil), "milkyway.restaking.v1.MsgDelegateServiceResponse") + proto.RegisterType((*MsgUpdateParams)(nil), "milkyway.restaking.v1.MsgUpdateParams") + proto.RegisterType((*MsgUpdateParamsResponse)(nil), "milkyway.restaking.v1.MsgUpdateParamsResponse") +} + +func init() { + proto.RegisterFile("milkyway/restaking/v1/messages.proto", fileDescriptor_9772be2b1a923bdb) +} + +var fileDescriptor_9772be2b1a923bdb = []byte{ + // 689 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x55, 0x3f, 0x6f, 0xd3, 0x4e, + 0x18, 0x8e, 0x9b, 0xdf, 0x2f, 0x52, 0xae, 0x7f, 0x31, 0x45, 0x6d, 0xdd, 0xd6, 0xae, 0x2c, 0x84, + 0x4a, 0xd4, 0xfa, 0x48, 0x2b, 0x81, 0x14, 0xa6, 0x86, 0x2e, 0xad, 0x14, 0x81, 0x5c, 0xb1, 0xb0, + 0x54, 0x97, 0xf8, 0xea, 0x5a, 0x89, 0x7d, 0x96, 0xcf, 0x0d, 0x64, 0x43, 0x8c, 0x4c, 0xac, 0x2c, + 0x7c, 0x00, 0xa6, 0x0e, 0xcc, 0x48, 0x6c, 0x1d, 0x2b, 0x58, 0x98, 0x02, 0x72, 0x87, 0xec, 0x7c, + 0x02, 0x64, 0xfb, 0x7c, 0x71, 0x12, 0x37, 0x8d, 0x3a, 0xb0, 0xb4, 0x79, 0xef, 0x7d, 0xde, 0x3f, + 0xcf, 0x93, 0x7b, 0x2e, 0xe0, 0xbe, 0x6d, 0xb5, 0x9a, 0x9d, 0xd7, 0xa8, 0x03, 0x3d, 0x4c, 0x7d, + 0xd4, 0xb4, 0x1c, 0x13, 0xb6, 0xcb, 0xd0, 0xc6, 0x94, 0x22, 0x13, 0x53, 0xcd, 0xf5, 0x88, 0x4f, + 0xc4, 0x7b, 0x09, 0x4a, 0xe3, 0x28, 0xad, 0x5d, 0x96, 0xee, 0x20, 0xdb, 0x72, 0x08, 0x8c, 0xfe, + 0xc6, 0x48, 0x69, 0xa5, 0x41, 0xa8, 0x4d, 0xe8, 0x71, 0x14, 0xc1, 0x38, 0x60, 0x29, 0x39, 0x8e, + 0x60, 0x1d, 0x51, 0x0c, 0xdb, 0xe5, 0x3a, 0xf6, 0x51, 0x19, 0x36, 0x88, 0xe5, 0x8c, 0xe4, 0x9d, + 0x26, 0xcf, 0x87, 0x01, 0xcb, 0x2f, 0xb1, 0xbc, 0x4d, 0xe3, 0x15, 0xa9, 0xc9, 0x12, 0x8b, 0x26, + 0x31, 0x49, 0x3c, 0x30, 0xfc, 0xc4, 0x4e, 0xd5, 0x6c, 0x66, 0x2e, 0xf2, 0x90, 0xcd, 0x56, 0x52, + 0xbf, 0x0a, 0x60, 0xbe, 0x46, 0xcd, 0x7d, 0xdc, 0xc2, 0x26, 0xf2, 0xf1, 0x0b, 0x42, 0x5a, 0xe2, + 0x63, 0x50, 0x34, 0xe2, 0x98, 0x78, 0xcb, 0xc2, 0x86, 0xb0, 0x59, 0xac, 0x2e, 0x7f, 0xff, 0xb2, + 0xbd, 0xc8, 0xb8, 0xec, 0x19, 0x86, 0x87, 0x29, 0x3d, 0xf2, 0x3d, 0xcb, 0x31, 0xf5, 0x3e, 0x54, + 0xdc, 0x03, 0x05, 0x64, 0x93, 0x33, 0xc7, 0x5f, 0x9e, 0xda, 0x10, 0x36, 0xa7, 0x77, 0x56, 0x34, + 0x56, 0x11, 0xf2, 0xd5, 0x18, 0x1f, 0xed, 0x19, 0xb1, 0x9c, 0xea, 0xdc, 0x45, 0x57, 0xc9, 0x05, + 0x5d, 0xa5, 0xb0, 0x17, 0x15, 0xe8, 0xac, 0xb0, 0xf2, 0xe8, 0x5d, 0xef, 0xbc, 0xd4, 0x6f, 0xf9, + 0xbe, 0x77, 0x5e, 0x5a, 0xe7, 0x2c, 0x6a, 0xd4, 0x3c, 0x24, 0x96, 0xa3, 0x27, 0x64, 0xc2, 0x65, + 0xd5, 0x15, 0xb0, 0x34, 0xb4, 0xbf, 0x8e, 0xa9, 0x4b, 0x1c, 0x8a, 0xd5, 0x4f, 0x53, 0xe0, 0x6e, + 0x2a, 0xf7, 0xdc, 0xc5, 0x5e, 0xb4, 0xe7, 0x6d, 0xf9, 0x41, 0x30, 0x4d, 0x58, 0x8f, 0x63, 0xcb, + 0x88, 0x48, 0xce, 0x56, 0xe7, 0x82, 0xae, 0x02, 0x92, 0xd6, 0x07, 0xfb, 0x3a, 0x48, 0x20, 0x07, + 0x86, 0x68, 0x73, 0x41, 0xf2, 0x1b, 0xf9, 0xf1, 0x82, 0x54, 0x06, 0x05, 0xf9, 0xfc, 0x4b, 0xd9, + 0x34, 0x2d, 0xff, 0xf4, 0xac, 0xae, 0x35, 0x88, 0xcd, 0x6e, 0x11, 0xfb, 0xb7, 0x4d, 0x8d, 0x26, + 0xf4, 0x3b, 0x2e, 0xa6, 0x51, 0x29, 0xe5, 0xe2, 0xc1, 0x51, 0xf1, 0xd6, 0xd2, 0xe2, 0x0d, 0x0b, + 0xa1, 0xae, 0x83, 0xd5, 0x8c, 0x63, 0xae, 0xdf, 0xc7, 0x29, 0x20, 0xa6, 0xf2, 0x47, 0xd8, 0x6b, + 0x5b, 0x0d, 0x7c, 0x6b, 0xf9, 0xb6, 0x00, 0xa0, 0x71, 0x8b, 0xbe, 0x7a, 0xb3, 0x41, 0x57, 0x29, + 0xb2, 0xc6, 0x07, 0xfb, 0x7a, 0x91, 0x01, 0xfe, 0xbd, 0x76, 0xda, 0xa8, 0x76, 0xab, 0x59, 0xda, + 0xb1, 0x5d, 0xd5, 0x35, 0x20, 0x8d, 0x9e, 0x72, 0xe5, 0x7e, 0xc4, 0xae, 0x7a, 0xe9, 0x1a, 0xe1, + 0x9d, 0x8c, 0xfc, 0x26, 0x1e, 0x82, 0x22, 0x3a, 0xf3, 0x4f, 0x89, 0x67, 0xf9, 0x1d, 0x26, 0xdb, + 0xd6, 0x9f, 0xae, 0xb2, 0xd0, 0x41, 0x76, 0xab, 0xa2, 0xf2, 0x94, 0x7a, 0xbd, 0x94, 0x1c, 0x23, + 0x3e, 0x05, 0x85, 0xd8, 0xc5, 0xcc, 0x69, 0xeb, 0x5a, 0xe6, 0xf3, 0xa4, 0xc5, 0xa3, 0xab, 0xff, + 0x85, 0x02, 0xe9, 0xac, 0xa4, 0xf2, 0x24, 0xa2, 0xca, 0x9b, 0x85, 0x54, 0xfb, 0x6f, 0xe0, 0x9b, + 0xd4, 0x5b, 0x31, 0xc4, 0x80, 0x59, 0x2d, 0x7d, 0x94, 0x10, 0xde, 0xf9, 0x96, 0x07, 0xf9, 0x1a, + 0x35, 0xc5, 0x13, 0x30, 0x33, 0xf0, 0x94, 0x3c, 0xb8, 0x66, 0xb1, 0x21, 0xcb, 0x4a, 0xda, 0x64, + 0xb8, 0x64, 0x9e, 0xe8, 0x81, 0x85, 0x11, 0x5b, 0x97, 0x6e, 0xee, 0x91, 0x60, 0xa5, 0x9d, 0xc9, + 0xb1, 0x7c, 0x26, 0x01, 0xf3, 0xc3, 0x56, 0x78, 0x78, 0x73, 0x1b, 0x06, 0x95, 0xca, 0x13, 0x43, + 0xf9, 0xc0, 0x13, 0x30, 0x33, 0x70, 0x83, 0xc6, 0x88, 0x99, 0xc6, 0x8d, 0x13, 0x33, 0xeb, 0xcb, + 0x93, 0xfe, 0x7f, 0xdb, 0x3b, 0x2f, 0x09, 0xd5, 0xda, 0x45, 0x20, 0x0b, 0x97, 0x81, 0x2c, 0xfc, + 0x0e, 0x64, 0xe1, 0xc3, 0x95, 0x9c, 0xbb, 0xbc, 0x92, 0x73, 0x3f, 0xaf, 0xe4, 0xdc, 0xab, 0xdd, + 0x94, 0x9d, 0x92, 0xd6, 0xdb, 0x2d, 0x54, 0xa7, 0x30, 0xf3, 0xde, 0x44, 0xfe, 0xaa, 0x17, 0xa2, + 0x1f, 0x98, 0xdd, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x9f, 0x6a, 0x1c, 0xc5, 0x60, 0x07, 0x00, + 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// MsgClient is the client API for Msg service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type MsgClient interface { + // DelegatePool defines the operation that allows users to delegate any amount + // of an asset to a pool that can then be used to provide services with + // cryptoeconomic security. + DelegatePool(ctx context.Context, in *MsgDelegatePool, opts ...grpc.CallOption) (*MsgDelegatePoolResponse, error) + // DelegateOperator defines the operation that allows users to delegate their + // assets to a specific operator. + DelegateOperator(ctx context.Context, in *MsgDelegateOperator, opts ...grpc.CallOption) (*MsgDelegateOperatorResponse, error) + // DelegateService defines the operation that allows users to delegate their + // assets to a specific service. + DelegateService(ctx context.Context, in *MsgDelegateService, opts ...grpc.CallOption) (*MsgDelegateServiceResponse, error) + // UpdateParams defines a (governance) operation for updating the module + // parameters. + // The authority defaults to the x/gov module account. + UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) DelegatePool(ctx context.Context, in *MsgDelegatePool, opts ...grpc.CallOption) (*MsgDelegatePoolResponse, error) { + out := new(MsgDelegatePoolResponse) + err := c.cc.Invoke(ctx, "/milkyway.restaking.v1.Msg/DelegatePool", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) DelegateOperator(ctx context.Context, in *MsgDelegateOperator, opts ...grpc.CallOption) (*MsgDelegateOperatorResponse, error) { + out := new(MsgDelegateOperatorResponse) + err := c.cc.Invoke(ctx, "/milkyway.restaking.v1.Msg/DelegateOperator", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) DelegateService(ctx context.Context, in *MsgDelegateService, opts ...grpc.CallOption) (*MsgDelegateServiceResponse, error) { + out := new(MsgDelegateServiceResponse) + err := c.cc.Invoke(ctx, "/milkyway.restaking.v1.Msg/DelegateService", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) { + out := new(MsgUpdateParamsResponse) + err := c.cc.Invoke(ctx, "/milkyway.restaking.v1.Msg/UpdateParams", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // DelegatePool defines the operation that allows users to delegate any amount + // of an asset to a pool that can then be used to provide services with + // cryptoeconomic security. + DelegatePool(context.Context, *MsgDelegatePool) (*MsgDelegatePoolResponse, error) + // DelegateOperator defines the operation that allows users to delegate their + // assets to a specific operator. + DelegateOperator(context.Context, *MsgDelegateOperator) (*MsgDelegateOperatorResponse, error) + // DelegateService defines the operation that allows users to delegate their + // assets to a specific service. + DelegateService(context.Context, *MsgDelegateService) (*MsgDelegateServiceResponse, error) + // UpdateParams defines a (governance) operation for updating the module + // parameters. + // The authority defaults to the x/gov module account. + UpdateParams(context.Context, *MsgUpdateParams) (*MsgUpdateParamsResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) DelegatePool(ctx context.Context, req *MsgDelegatePool) (*MsgDelegatePoolResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DelegatePool not implemented") +} +func (*UnimplementedMsgServer) DelegateOperator(ctx context.Context, req *MsgDelegateOperator) (*MsgDelegateOperatorResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DelegateOperator not implemented") +} +func (*UnimplementedMsgServer) DelegateService(ctx context.Context, req *MsgDelegateService) (*MsgDelegateServiceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DelegateService not implemented") +} +func (*UnimplementedMsgServer) UpdateParams(ctx context.Context, req *MsgUpdateParams) (*MsgUpdateParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateParams not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_DelegatePool_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgDelegatePool) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).DelegatePool(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/milkyway.restaking.v1.Msg/DelegatePool", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).DelegatePool(ctx, req.(*MsgDelegatePool)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_DelegateOperator_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgDelegateOperator) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).DelegateOperator(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/milkyway.restaking.v1.Msg/DelegateOperator", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).DelegateOperator(ctx, req.(*MsgDelegateOperator)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_DelegateService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgDelegateService) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).DelegateService(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/milkyway.restaking.v1.Msg/DelegateService", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).DelegateService(ctx, req.(*MsgDelegateService)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_UpdateParams_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgUpdateParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).UpdateParams(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/milkyway.restaking.v1.Msg/UpdateParams", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).UpdateParams(ctx, req.(*MsgUpdateParams)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "milkyway.restaking.v1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "DelegatePool", + Handler: _Msg_DelegatePool_Handler, + }, + { + MethodName: "DelegateOperator", + Handler: _Msg_DelegateOperator_Handler, + }, + { + MethodName: "DelegateService", + Handler: _Msg_DelegateService_Handler, + }, + { + MethodName: "UpdateParams", + Handler: _Msg_UpdateParams_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "milkyway/restaking/v1/messages.proto", +} + +func (m *MsgDelegatePool) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgDelegatePool) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgDelegatePool) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Amount.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMessages(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Delegator) > 0 { + i -= len(m.Delegator) + copy(dAtA[i:], m.Delegator) + i = encodeVarintMessages(dAtA, i, uint64(len(m.Delegator))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgDelegatePoolResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgDelegatePoolResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgDelegatePoolResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgDelegateOperator) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgDelegateOperator) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgDelegateOperator) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Amount) > 0 { + for iNdEx := len(m.Amount) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Amount[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMessages(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if m.OperatorID != 0 { + i = encodeVarintMessages(dAtA, i, uint64(m.OperatorID)) + i-- + dAtA[i] = 0x10 + } + if len(m.Delegator) > 0 { + i -= len(m.Delegator) + copy(dAtA[i:], m.Delegator) + i = encodeVarintMessages(dAtA, i, uint64(len(m.Delegator))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgDelegateOperatorResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgDelegateOperatorResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgDelegateOperatorResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgDelegateService) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgDelegateService) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgDelegateService) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Amount) > 0 { + for iNdEx := len(m.Amount) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Amount[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMessages(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if m.ServiceID != 0 { + i = encodeVarintMessages(dAtA, i, uint64(m.ServiceID)) + i-- + dAtA[i] = 0x10 + } + if len(m.Delegator) > 0 { + i -= len(m.Delegator) + copy(dAtA[i:], m.Delegator) + i = encodeVarintMessages(dAtA, i, uint64(len(m.Delegator))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgDelegateServiceResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgDelegateServiceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgDelegateServiceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgUpdateParams) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgUpdateParams) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMessages(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Authority) > 0 { + i -= len(m.Authority) + copy(dAtA[i:], m.Authority) + i = encodeVarintMessages(dAtA, i, uint64(len(m.Authority))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgUpdateParamsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgUpdateParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func encodeVarintMessages(dAtA []byte, offset int, v uint64) int { + offset -= sovMessages(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgDelegatePool) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Delegator) + if l > 0 { + n += 1 + l + sovMessages(uint64(l)) + } + l = m.Amount.Size() + n += 1 + l + sovMessages(uint64(l)) + return n +} + +func (m *MsgDelegatePoolResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgDelegateOperator) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Delegator) + if l > 0 { + n += 1 + l + sovMessages(uint64(l)) + } + if m.OperatorID != 0 { + n += 1 + sovMessages(uint64(m.OperatorID)) + } + if len(m.Amount) > 0 { + for _, e := range m.Amount { + l = e.Size() + n += 1 + l + sovMessages(uint64(l)) + } + } + return n +} + +func (m *MsgDelegateOperatorResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgDelegateService) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Delegator) + if l > 0 { + n += 1 + l + sovMessages(uint64(l)) + } + if m.ServiceID != 0 { + n += 1 + sovMessages(uint64(m.ServiceID)) + } + if len(m.Amount) > 0 { + for _, e := range m.Amount { + l = e.Size() + n += 1 + l + sovMessages(uint64(l)) + } + } + return n +} + +func (m *MsgDelegateServiceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgUpdateParams) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Authority) + if l > 0 { + n += 1 + l + sovMessages(uint64(l)) + } + l = m.Params.Size() + n += 1 + l + sovMessages(uint64(l)) + return n +} + +func (m *MsgUpdateParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func sovMessages(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozMessages(x uint64) (n int) { + return sovMessages(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgDelegatePool) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessages + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgDelegatePool: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgDelegatePool: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Delegator", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessages + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMessages + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMessages + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Delegator = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessages + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMessages + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMessages + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMessages(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMessages + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgDelegatePoolResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessages + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgDelegatePoolResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgDelegatePoolResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipMessages(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMessages + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgDelegateOperator) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessages + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgDelegateOperator: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgDelegateOperator: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Delegator", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessages + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMessages + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMessages + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Delegator = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OperatorID", wireType) + } + m.OperatorID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessages + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.OperatorID |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessages + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMessages + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMessages + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Amount = append(m.Amount, types.Coin{}) + if err := m.Amount[len(m.Amount)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMessages(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMessages + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgDelegateOperatorResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessages + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgDelegateOperatorResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgDelegateOperatorResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipMessages(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMessages + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgDelegateService) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessages + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgDelegateService: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgDelegateService: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Delegator", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessages + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMessages + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMessages + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Delegator = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ServiceID", wireType) + } + m.ServiceID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessages + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ServiceID |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessages + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMessages + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMessages + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Amount = append(m.Amount, types.Coin{}) + if err := m.Amount[len(m.Amount)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMessages(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMessages + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgDelegateServiceResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessages + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgDelegateServiceResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgDelegateServiceResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipMessages(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMessages + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgUpdateParams) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessages + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgUpdateParams: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpdateParams: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Authority", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessages + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMessages + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMessages + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Authority = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessages + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMessages + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMessages + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMessages(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMessages + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgUpdateParamsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessages + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgUpdateParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpdateParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipMessages(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMessages + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipMessages(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMessages + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMessages + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMessages + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthMessages + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupMessages + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthMessages + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthMessages = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowMessages = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupMessages = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/restaking/types/messages_test.go b/x/restaking/types/messages_test.go new file mode 100644 index 000000000..c6391ba4d --- /dev/null +++ b/x/restaking/types/messages_test.go @@ -0,0 +1,265 @@ +package types_test + +import ( + "testing" + + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + + "github.com/milkyway-labs/milkyway/x/restaking/types" +) + +var msgDelegatePool = types.NewMsgDelegatePool( + sdk.NewCoin("umilk", sdkmath.NewInt(100_000_000)), + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", +) + +func TestMsgDelegatePool_ValidateBasic(t *testing.T) { + testCases := []struct { + name string + msg *types.MsgDelegatePool + shouldErr bool + }{ + { + name: "invalid amount returns error", + msg: types.NewMsgDelegatePool( + sdk.Coin{Denom: "invalid!", Amount: sdkmath.NewInt(100_000_000)}, + msgDelegatePool.Delegator, + ), + shouldErr: true, + }, + { + name: "invalid delegator address returns error", + msg: types.NewMsgDelegatePool( + msgDelegatePool.Amount, + "invalid", + ), + shouldErr: true, + }, + { + name: "valid message returns no error", + msg: msgDelegatePool, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + err := tc.msg.ValidateBasic() + if tc.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestMsgDelegatePool_GetSignBytes(t *testing.T) { + expected := `{"type":"milkyway/MsgDelegatePool","value":{"amount":{"amount":"100000000","denom":"umilk"},"delegator":"cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd"}}` + require.Equal(t, expected, string(msgDelegatePool.GetSignBytes())) +} + +func TestMsgDelegatePool_GetSigners(t *testing.T) { + addr, _ := sdk.AccAddressFromBech32(msgDelegatePool.Delegator) + require.Equal(t, []sdk.AccAddress{addr}, msgDelegatePool.GetSigners()) +} + +// -------------------------------------------------------------------------------------------------------------------- + +var msgDelegateOperator = types.NewMsgDelegateOperator( + 1, + sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100_000_000))), + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", +) + +func TestMsgDelegateOperator_ValidateBasic(t *testing.T) { + testCases := []struct { + name string + msg *types.MsgDelegateOperator + shouldErr bool + }{ + { + name: "invalid operator id returns error", + msg: types.NewMsgDelegateOperator( + 0, + msgDelegateOperator.Amount, + msgDelegateOperator.Delegator, + ), + shouldErr: true, + }, + { + name: "invalid amount returns error", + msg: types.NewMsgDelegateOperator( + msgDelegateOperator.OperatorID, + sdk.Coins{sdk.Coin{Denom: "invalid!", Amount: sdkmath.NewInt(100_000_000)}}, + msgDelegateOperator.Delegator, + ), + shouldErr: true, + }, + { + name: "invalid delegator address returns error", + msg: types.NewMsgDelegateOperator( + msgDelegateOperator.OperatorID, + msgDelegateOperator.Amount, + "invalid", + ), + shouldErr: true, + }, + { + name: "valid message returns no error", + msg: msgDelegateOperator, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + err := tc.msg.ValidateBasic() + if tc.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestMsgDelegateOperator_GetSignBytes(t *testing.T) { + expected := `{"type":"milkyway/MsgDelegateOperator","value":{"amount":[{"amount":"100000000","denom":"umilk"}],"delegator":"cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd","operator_id":1}}` + require.Equal(t, expected, string(msgDelegateOperator.GetSignBytes())) +} + +func TestMsgDelegateOperator_GetSigners(t *testing.T) { + addr, _ := sdk.AccAddressFromBech32(msgDelegateOperator.Delegator) + require.Equal(t, []sdk.AccAddress{addr}, msgDelegateOperator.GetSigners()) +} + +// -------------------------------------------------------------------------------------------------------------------- + +var msgDelegateService = types.NewMsgDelegateService( + 1, + sdk.NewCoins(sdk.NewCoin("umilk", sdkmath.NewInt(100_000_000))), + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", +) + +func TestMsgDelegateService_ValidateBasic(t *testing.T) { + testCases := []struct { + name string + msg *types.MsgDelegateService + shouldErr bool + }{ + { + name: "invalid service id returns error", + msg: types.NewMsgDelegateService( + 0, + msgDelegateService.Amount, + msgDelegateService.Delegator, + ), + shouldErr: true, + }, + { + name: "invalid amount returns error", + msg: types.NewMsgDelegateService( + msgDelegateService.ServiceID, + sdk.Coins{sdk.Coin{Denom: "invalid!", Amount: sdkmath.NewInt(100_000_000)}}, + msgDelegateService.Delegator, + ), + shouldErr: true, + }, + { + name: "invalid delegator address returns error", + msg: types.NewMsgDelegateService( + msgDelegateService.ServiceID, + msgDelegateService.Amount, + "invalid", + ), + shouldErr: true, + }, + { + name: "valid message returns no error", + msg: msgDelegateService, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + err := tc.msg.ValidateBasic() + if tc.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestMsgDelegateService_GetSignBytes(t *testing.T) { + expected := `{"type":"milkyway/MsgDelegateService","value":{"amount":[{"amount":"100000000","denom":"umilk"}],"delegator":"cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd","service_id":1}}` + require.Equal(t, expected, string(msgDelegateService.GetSignBytes())) +} + +func TestMsgDelegateService_GetSigners(t *testing.T) { + addr, _ := sdk.AccAddressFromBech32(msgDelegateService.Delegator) + require.Equal(t, []sdk.AccAddress{addr}, msgDelegateOperator.GetSigners()) +} + +// -------------------------------------------------------------------------------------------------------------------- + +var msgUpdateParams = types.NewMsgUpdateParams( + types.DefaultParams(), + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", +) + +func TestMsgUpdateParams_ValidateBasic(t *testing.T) { + testCases := []struct { + name string + msg *types.MsgUpdateParams + shouldErr bool + }{ + { + name: "invalid params return error", + msg: types.NewMsgUpdateParams( + types.NewParams(0), + msgUpdateParams.Authority, + ), + shouldErr: true, + }, + { + name: "invalid authority address returns error", + msg: types.NewMsgUpdateParams( + msgUpdateParams.Params, + "invalid", + ), + shouldErr: true, + }, + { + name: "valid message returns no error", + msg: msgUpdateParams, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + err := tc.msg.ValidateBasic() + if tc.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestMsgUpdateParams_GetSignBytes(t *testing.T) { + expected := `{"type":"milkyway/restaking/MsgUpdateParams","value":{"authority":"cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd","params":{"unbonding_time":"259200000000000"}}}` + require.Equal(t, expected, string(msgUpdateParams.GetSignBytes())) +} + +func TestMsgUpdateParams_GetSigners(t *testing.T) { + addr, _ := sdk.AccAddressFromBech32(msgUpdateParams.Authority) + require.Equal(t, []sdk.AccAddress{addr}, msgDelegateOperator.GetSigners()) +} diff --git a/x/restaking/types/models.go b/x/restaking/types/models.go new file mode 100644 index 000000000..0df899b75 --- /dev/null +++ b/x/restaking/types/models.go @@ -0,0 +1,183 @@ +package types + +import ( + "fmt" + + sdkmath "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// NewPoolDelegation creates a new PoolDelegation instance +func NewPoolDelegation(poolID uint32, userAddress string, shares sdkmath.LegacyDec) PoolDelegation { + return PoolDelegation{ + PoolID: poolID, + UserAddress: userAddress, + Shares: shares, + } +} + +// isDelegation implements Delegation +func (d PoolDelegation) isDelegation() {} + +// Validate validates the pool delegation +func (d PoolDelegation) Validate() error { + if d.PoolID == 0 { + return fmt.Errorf("invalid pool id") + } + + _, err := sdk.AccAddressFromBech32(d.UserAddress) + if err != nil { + return fmt.Errorf("invalid user address: %s", d.UserAddress) + } + + if d.Shares.IsNegative() { + return ErrInvalidShares + } + + return nil +} + +// MustMarshalPoolDelegation marshals the given pool delegation using the provided codec +func MustMarshalPoolDelegation(cdc codec.BinaryCodec, delegation PoolDelegation) []byte { + bz, err := cdc.Marshal(&delegation) + if err != nil { + panic(err) + } + return bz +} + +// UnmarshalPoolDelegation unmarshals a pool delegation from the given bytes using the provided codec +func UnmarshalPoolDelegation(cdc codec.BinaryCodec, bz []byte) (PoolDelegation, error) { + var delegation PoolDelegation + err := cdc.Unmarshal(bz, &delegation) + if err != nil { + return PoolDelegation{}, err + } + return delegation, nil +} + +// MustUnmarshalPoolDelegation unmarshals a pool delegation from the given bytes using the provided codec +func MustUnmarshalPoolDelegation(cdc codec.BinaryCodec, bz []byte) PoolDelegation { + delegation, err := UnmarshalPoolDelegation(cdc, bz) + if err != nil { + panic(err) + } + return delegation +} + +// -------------------------------------------------------------------------------------------------------------------- + +func NewOperatorDelegation(operatorID uint32, userAddress string, shares sdk.DecCoins) OperatorDelegation { + return OperatorDelegation{ + OperatorID: operatorID, + UserAddress: userAddress, + Shares: shares, + } +} + +func (d OperatorDelegation) isDelegation() {} + +func (d OperatorDelegation) Validate() error { + if d.OperatorID == 0 { + return fmt.Errorf("invalid operator id") + } + + _, err := sdk.AccAddressFromBech32(d.UserAddress) + if err != nil { + return fmt.Errorf("invalid user address: %s", d.UserAddress) + } + + if d.Shares.IsAnyNegative() { + return ErrInvalidShares + } + + return nil +} + +// MustMarshalOperatorDelegation marshals the given operator delegation using the provided codec +func MustMarshalOperatorDelegation(cdc codec.BinaryCodec, delegation OperatorDelegation) []byte { + bz, err := cdc.Marshal(&delegation) + if err != nil { + panic(err) + } + return bz +} + +// UnmarshalOperatorDelegation unmarshals an operator delegation from the given bytes using the provided codec +func UnmarshalOperatorDelegation(cdc codec.BinaryCodec, bz []byte) (OperatorDelegation, error) { + var delegation OperatorDelegation + err := cdc.Unmarshal(bz, &delegation) + if err != nil { + return OperatorDelegation{}, err + } + return delegation, nil +} + +// MustUnmarshalOperatorDelegation unmarshals an operator delegation from the given bytes using the provided codec +func MustUnmarshalOperatorDelegation(cdc codec.BinaryCodec, bz []byte) OperatorDelegation { + delegation, err := UnmarshalOperatorDelegation(cdc, bz) + if err != nil { + panic(err) + } + return delegation +} + +// -------------------------------------------------------------------------------------------------------------------- + +func NewServiceDelegation(serviceID uint32, userAddress string, shares sdk.DecCoins) ServiceDelegation { + return ServiceDelegation{ + ServiceID: serviceID, + UserAddress: userAddress, + Shares: shares, + } +} + +// isDelegation implements Delegation +func (d ServiceDelegation) isDelegation() {} + +// Validate validates the service delegation +func (d ServiceDelegation) Validate() error { + if d.ServiceID == 0 { + return fmt.Errorf("invalid service id") + } + + _, err := sdk.AccAddressFromBech32(d.UserAddress) + if err != nil { + return fmt.Errorf("invalid user address: %s", d.UserAddress) + } + + if d.Shares.IsAnyNegative() { + return ErrInvalidShares + } + + return nil +} + +// MustMarshalServiceDelegation marshals the given service delegation using the provided codec +func MustMarshalServiceDelegation(cdc codec.BinaryCodec, delegation ServiceDelegation) []byte { + bz, err := cdc.Marshal(&delegation) + if err != nil { + panic(err) + } + return bz +} + +// UnmarshalServiceDelegation unmarshals a service delegation from the given bytes using the provided codec +func UnmarshalServiceDelegation(cdc codec.BinaryCodec, bz []byte) (ServiceDelegation, error) { + var delegation ServiceDelegation + err := cdc.Unmarshal(bz, &delegation) + if err != nil { + return ServiceDelegation{}, err + } + return delegation, nil +} + +// MustUnmarshalServiceDelegation unmarshals a service delegation from the given bytes using the provided codec +func MustUnmarshalServiceDelegation(cdc codec.BinaryCodec, bz []byte) ServiceDelegation { + delegation, err := UnmarshalServiceDelegation(cdc, bz) + if err != nil { + panic(err) + } + return delegation +} diff --git a/x/restaking/types/models.pb.go b/x/restaking/types/models.pb.go new file mode 100644 index 000000000..e22875135 --- /dev/null +++ b/x/restaking/types/models.pb.go @@ -0,0 +1,919 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: milkyway/restaking/v1/models.proto + +package types + +import ( + cosmossdk_io_math "cosmossdk.io/math" + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/cosmos-sdk/types/tx/amino" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// PoolDelegation represents the bond with tokens held by an account with a +// given pool. It is owned by one delegator, and is associated with a pool. +type PoolDelegation struct { + // UserAddress is the encoded address of the user. + UserAddress string `protobuf:"bytes,1,opt,name=user_address,json=userAddress,proto3" json:"user_address,omitempty"` + // PoolID is the id of the pool. + PoolID uint32 `protobuf:"varint,2,opt,name=pool_id,json=poolId,proto3" json:"pool_id,omitempty"` + // Shares define the delegation shares received. + Shares cosmossdk_io_math.LegacyDec `protobuf:"bytes,3,opt,name=shares,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"shares"` +} + +func (m *PoolDelegation) Reset() { *m = PoolDelegation{} } +func (m *PoolDelegation) String() string { return proto.CompactTextString(m) } +func (*PoolDelegation) ProtoMessage() {} +func (*PoolDelegation) Descriptor() ([]byte, []int) { + return fileDescriptor_86f4cd48423b1e2f, []int{0} +} +func (m *PoolDelegation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PoolDelegation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PoolDelegation.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PoolDelegation) XXX_Merge(src proto.Message) { + xxx_messageInfo_PoolDelegation.Merge(m, src) +} +func (m *PoolDelegation) XXX_Size() int { + return m.Size() +} +func (m *PoolDelegation) XXX_DiscardUnknown() { + xxx_messageInfo_PoolDelegation.DiscardUnknown(m) +} + +var xxx_messageInfo_PoolDelegation proto.InternalMessageInfo + +// OperatorDelegation represents the bond with tokens held by an account with a +// given operator. It is owned by one delegator, and is associated with a +// operator. +type OperatorDelegation struct { + // UserAddress is the encoded address of the user. + UserAddress string `protobuf:"bytes,1,opt,name=user_address,json=userAddress,proto3" json:"user_address,omitempty"` + // OperatorID is the id of the operator. + OperatorID uint32 `protobuf:"varint,2,opt,name=operator_id,json=operatorId,proto3" json:"operator_id,omitempty"` + // Shares define the delegation shares received. + Shares github_com_cosmos_cosmos_sdk_types.DecCoins `protobuf:"bytes,3,rep,name=shares,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.DecCoins" json:"shares"` +} + +func (m *OperatorDelegation) Reset() { *m = OperatorDelegation{} } +func (m *OperatorDelegation) String() string { return proto.CompactTextString(m) } +func (*OperatorDelegation) ProtoMessage() {} +func (*OperatorDelegation) Descriptor() ([]byte, []int) { + return fileDescriptor_86f4cd48423b1e2f, []int{1} +} +func (m *OperatorDelegation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *OperatorDelegation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_OperatorDelegation.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *OperatorDelegation) XXX_Merge(src proto.Message) { + xxx_messageInfo_OperatorDelegation.Merge(m, src) +} +func (m *OperatorDelegation) XXX_Size() int { + return m.Size() +} +func (m *OperatorDelegation) XXX_DiscardUnknown() { + xxx_messageInfo_OperatorDelegation.DiscardUnknown(m) +} + +var xxx_messageInfo_OperatorDelegation proto.InternalMessageInfo + +// ServiceDelegation represents the bond with tokens held by an account with a +// given service. It is owned by one delegator, and is associated with a +// service. +type ServiceDelegation struct { + // UserAddress is the encoded address of the user. + UserAddress string `protobuf:"bytes,1,opt,name=user_address,json=userAddress,proto3" json:"user_address,omitempty"` + // ServiceID is the id of the service. + ServiceID uint32 `protobuf:"varint,2,opt,name=service_id,json=serviceId,proto3" json:"service_id,omitempty"` + // Shares define the delegation shares received. + Shares github_com_cosmos_cosmos_sdk_types.DecCoins `protobuf:"bytes,3,rep,name=shares,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.DecCoins" json:"shares"` +} + +func (m *ServiceDelegation) Reset() { *m = ServiceDelegation{} } +func (m *ServiceDelegation) String() string { return proto.CompactTextString(m) } +func (*ServiceDelegation) ProtoMessage() {} +func (*ServiceDelegation) Descriptor() ([]byte, []int) { + return fileDescriptor_86f4cd48423b1e2f, []int{2} +} +func (m *ServiceDelegation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ServiceDelegation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ServiceDelegation.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ServiceDelegation) XXX_Merge(src proto.Message) { + xxx_messageInfo_ServiceDelegation.Merge(m, src) +} +func (m *ServiceDelegation) XXX_Size() int { + return m.Size() +} +func (m *ServiceDelegation) XXX_DiscardUnknown() { + xxx_messageInfo_ServiceDelegation.DiscardUnknown(m) +} + +var xxx_messageInfo_ServiceDelegation proto.InternalMessageInfo + +func init() { + proto.RegisterType((*PoolDelegation)(nil), "milkyway.restaking.v1.PoolDelegation") + proto.RegisterType((*OperatorDelegation)(nil), "milkyway.restaking.v1.OperatorDelegation") + proto.RegisterType((*ServiceDelegation)(nil), "milkyway.restaking.v1.ServiceDelegation") +} + +func init() { + proto.RegisterFile("milkyway/restaking/v1/models.proto", fileDescriptor_86f4cd48423b1e2f) +} + +var fileDescriptor_86f4cd48423b1e2f = []byte{ + // 473 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x93, 0x31, 0x6f, 0xd3, 0x40, + 0x14, 0xc7, 0x7d, 0x54, 0x0a, 0xe4, 0x42, 0x2b, 0xd5, 0x2a, 0x52, 0x28, 0xc8, 0xae, 0xc2, 0x12, + 0x09, 0xe2, 0x93, 0xc9, 0x06, 0x13, 0xc1, 0x8b, 0x25, 0x10, 0xc8, 0xdd, 0x58, 0xa2, 0xb3, 0x7d, + 0x72, 0x4e, 0xb1, 0xfd, 0xa2, 0xbb, 0x6b, 0xc0, 0xdf, 0x80, 0x91, 0x8f, 0xd0, 0x99, 0xb9, 0xdf, + 0x81, 0x8e, 0x55, 0x27, 0xc4, 0x60, 0x90, 0xb3, 0xb0, 0x33, 0xb0, 0x22, 0xdb, 0xe7, 0x36, 0x03, + 0x63, 0xa5, 0x2e, 0xb6, 0xdf, 0x7b, 0x7f, 0xff, 0xff, 0xfa, 0xdd, 0xe9, 0xe1, 0x51, 0xc6, 0xd3, + 0x65, 0xf1, 0x91, 0x16, 0x44, 0x30, 0xa9, 0xe8, 0x92, 0xe7, 0x09, 0x59, 0xbb, 0x24, 0x83, 0x98, + 0xa5, 0xd2, 0x59, 0x09, 0x50, 0x60, 0x3e, 0xe8, 0x34, 0xce, 0x95, 0xc6, 0x59, 0xbb, 0x87, 0xfb, + 0x34, 0xe3, 0x39, 0x90, 0xe6, 0xd9, 0x2a, 0x0f, 0x1f, 0x46, 0x20, 0x33, 0x90, 0xf3, 0xa6, 0x22, + 0x6d, 0xa1, 0x47, 0x07, 0x09, 0x24, 0xd0, 0xf6, 0xeb, 0x2f, 0xdd, 0xb5, 0x5a, 0x0d, 0x09, 0xa9, + 0x64, 0x64, 0xed, 0x86, 0x4c, 0x51, 0x97, 0x44, 0xc0, 0xf3, 0x76, 0x3e, 0xfa, 0x86, 0xf0, 0xde, + 0x7b, 0x80, 0xd4, 0x63, 0x29, 0x4b, 0xa8, 0xe2, 0x90, 0x9b, 0x2f, 0xf1, 0xfd, 0x13, 0xc9, 0xc4, + 0x9c, 0xc6, 0xb1, 0x60, 0x52, 0x0e, 0xd1, 0x11, 0x1a, 0xf7, 0x67, 0xc3, 0xcb, 0xb3, 0xc9, 0x81, + 0x0e, 0x7c, 0xd5, 0x4e, 0x8e, 0x95, 0xe0, 0x79, 0x12, 0x0c, 0x6a, 0xb5, 0x6e, 0x99, 0x4f, 0xf0, + 0xdd, 0x15, 0x40, 0x3a, 0xe7, 0xf1, 0xf0, 0xce, 0x11, 0x1a, 0xef, 0xce, 0x70, 0x55, 0xda, 0xbd, + 0x3a, 0xc1, 0xf7, 0x82, 0x5e, 0x3d, 0xf2, 0x63, 0xd3, 0xc7, 0x3d, 0xb9, 0xa0, 0x82, 0xc9, 0xe1, + 0x4e, 0xe3, 0xed, 0x9e, 0x97, 0xb6, 0xf1, 0xa3, 0xb4, 0x1f, 0xb5, 0xfe, 0x32, 0x5e, 0x3a, 0x1c, + 0x48, 0x46, 0xd5, 0xc2, 0x79, 0xc3, 0x12, 0x1a, 0x15, 0x1e, 0x8b, 0x2e, 0xcf, 0x26, 0x58, 0xc7, + 0x7b, 0x2c, 0x0a, 0xb4, 0xc1, 0x8b, 0x7b, 0x9f, 0x4f, 0x6d, 0xe3, 0xf7, 0xa9, 0x6d, 0x8c, 0xfe, + 0x22, 0x6c, 0xbe, 0x5b, 0x31, 0x41, 0x15, 0x88, 0x9b, 0xa2, 0x21, 0x78, 0x00, 0xda, 0xf2, 0x9a, + 0x68, 0xaf, 0x2a, 0x6d, 0xdc, 0x25, 0xf9, 0x5e, 0x80, 0x3b, 0x89, 0x1f, 0x9b, 0x7c, 0x8b, 0x6c, + 0x67, 0x3c, 0x78, 0xfe, 0xd8, 0xd1, 0x21, 0xf5, 0xf9, 0x3b, 0xfa, 0xfc, 0x6b, 0x80, 0xd7, 0xc0, + 0xf3, 0xd9, 0xb4, 0xe6, 0xfe, 0xfa, 0xd3, 0x7e, 0x9a, 0x70, 0xb5, 0x38, 0x09, 0x9d, 0x08, 0x32, + 0x7d, 0xa7, 0xfa, 0x35, 0x91, 0xf1, 0x92, 0xa8, 0x62, 0xc5, 0x64, 0xf7, 0x8f, 0xfc, 0x0f, 0xf9, + 0x1f, 0x84, 0xf7, 0x8f, 0x99, 0x58, 0xf3, 0x88, 0xdd, 0x14, 0xf8, 0x33, 0x8c, 0x65, 0xeb, 0x78, + 0xcd, 0xbd, 0x5b, 0x95, 0x76, 0x5f, 0xe7, 0xf8, 0x5e, 0xd0, 0xd7, 0x82, 0x5b, 0xa2, 0x9e, 0xbd, + 0x3d, 0xaf, 0x2c, 0x74, 0x51, 0x59, 0xe8, 0x57, 0x65, 0xa1, 0x2f, 0x1b, 0xcb, 0xb8, 0xd8, 0x58, + 0xc6, 0xf7, 0x8d, 0x65, 0x7c, 0x98, 0x6e, 0x19, 0x77, 0x9b, 0x35, 0x49, 0x69, 0x28, 0xaf, 0x2a, + 0xf2, 0x69, 0x6b, 0x1b, 0x9b, 0xa4, 0xb0, 0xd7, 0xec, 0xc3, 0xf4, 0x5f, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x09, 0xdd, 0x16, 0xcb, 0xb0, 0x03, 0x00, 0x00, +} + +func (m *PoolDelegation) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PoolDelegation) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PoolDelegation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.Shares.Size() + i -= size + if _, err := m.Shares.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintModels(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if m.PoolID != 0 { + i = encodeVarintModels(dAtA, i, uint64(m.PoolID)) + i-- + dAtA[i] = 0x10 + } + if len(m.UserAddress) > 0 { + i -= len(m.UserAddress) + copy(dAtA[i:], m.UserAddress) + i = encodeVarintModels(dAtA, i, uint64(len(m.UserAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *OperatorDelegation) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *OperatorDelegation) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *OperatorDelegation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Shares) > 0 { + for iNdEx := len(m.Shares) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Shares[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintModels(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if m.OperatorID != 0 { + i = encodeVarintModels(dAtA, i, uint64(m.OperatorID)) + i-- + dAtA[i] = 0x10 + } + if len(m.UserAddress) > 0 { + i -= len(m.UserAddress) + copy(dAtA[i:], m.UserAddress) + i = encodeVarintModels(dAtA, i, uint64(len(m.UserAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ServiceDelegation) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ServiceDelegation) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ServiceDelegation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Shares) > 0 { + for iNdEx := len(m.Shares) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Shares[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintModels(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if m.ServiceID != 0 { + i = encodeVarintModels(dAtA, i, uint64(m.ServiceID)) + i-- + dAtA[i] = 0x10 + } + if len(m.UserAddress) > 0 { + i -= len(m.UserAddress) + copy(dAtA[i:], m.UserAddress) + i = encodeVarintModels(dAtA, i, uint64(len(m.UserAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintModels(dAtA []byte, offset int, v uint64) int { + offset -= sovModels(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *PoolDelegation) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.UserAddress) + if l > 0 { + n += 1 + l + sovModels(uint64(l)) + } + if m.PoolID != 0 { + n += 1 + sovModels(uint64(m.PoolID)) + } + l = m.Shares.Size() + n += 1 + l + sovModels(uint64(l)) + return n +} + +func (m *OperatorDelegation) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.UserAddress) + if l > 0 { + n += 1 + l + sovModels(uint64(l)) + } + if m.OperatorID != 0 { + n += 1 + sovModels(uint64(m.OperatorID)) + } + if len(m.Shares) > 0 { + for _, e := range m.Shares { + l = e.Size() + n += 1 + l + sovModels(uint64(l)) + } + } + return n +} + +func (m *ServiceDelegation) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.UserAddress) + if l > 0 { + n += 1 + l + sovModels(uint64(l)) + } + if m.ServiceID != 0 { + n += 1 + sovModels(uint64(m.ServiceID)) + } + if len(m.Shares) > 0 { + for _, e := range m.Shares { + l = e.Size() + n += 1 + l + sovModels(uint64(l)) + } + } + return n +} + +func sovModels(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozModels(x uint64) (n int) { + return sovModels(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *PoolDelegation) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PoolDelegation: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PoolDelegation: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UserAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthModels + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthModels + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UserAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PoolID", wireType) + } + m.PoolID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PoolID |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Shares", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthModels + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthModels + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Shares.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipModels(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthModels + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *OperatorDelegation) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: OperatorDelegation: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: OperatorDelegation: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UserAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthModels + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthModels + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UserAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OperatorID", wireType) + } + m.OperatorID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.OperatorID |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Shares", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthModels + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthModels + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Shares = append(m.Shares, types.DecCoin{}) + if err := m.Shares[len(m.Shares)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipModels(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthModels + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ServiceDelegation) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ServiceDelegation: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ServiceDelegation: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UserAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthModels + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthModels + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UserAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ServiceID", wireType) + } + m.ServiceID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ServiceID |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Shares", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthModels + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthModels + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Shares = append(m.Shares, types.DecCoin{}) + if err := m.Shares[len(m.Shares)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipModels(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthModels + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipModels(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowModels + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowModels + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowModels + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthModels + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupModels + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthModels + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthModels = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowModels = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupModels = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/restaking/types/models_test.go b/x/restaking/types/models_test.go new file mode 100644 index 000000000..4e6823ba3 --- /dev/null +++ b/x/restaking/types/models_test.go @@ -0,0 +1,183 @@ +package types_test + +import ( + "testing" + + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + + "github.com/milkyway-labs/milkyway/x/restaking/types" +) + +func TestPoolDelegation_Validate(t *testing.T) { + testCases := []struct { + name string + entry types.PoolDelegation + shouldErr bool + }{ + { + name: "invalid pool id returns error", + entry: types.NewPoolDelegation( + 0, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdkmath.LegacyNewDec(100), + ), + shouldErr: true, + }, + { + name: "invalid user address returns error", + entry: types.NewPoolDelegation( + 1, + "", + sdkmath.LegacyNewDec(100), + ), + shouldErr: true, + }, + { + name: "invalid shares returns error", + entry: types.NewPoolDelegation( + 1, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdkmath.LegacyNewDec(-100), + ), + shouldErr: true, + }, + { + name: "valid entry returns no error", + entry: types.NewPoolDelegation( + 1, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdkmath.LegacyNewDec(100), + ), + shouldErr: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := tc.entry.Validate() + if tc.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +// -------------------------------------------------------------------------------------------------------------------- + +func TestServiceDelegation_Validate(t *testing.T) { + testCases := []struct { + name string + entry types.ServiceDelegation + shouldErr bool + }{ + { + name: "invalid service id returns error", + entry: types.NewServiceDelegation( + 0, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdk.NewDecCoins(sdk.NewDecCoinFromDec("umilk", sdkmath.LegacyNewDec(100))), + ), + shouldErr: true, + }, + { + name: "invalid user address returns error", + entry: types.NewServiceDelegation( + 1, + "", + sdk.NewDecCoins(sdk.NewDecCoinFromDec("umilk", sdkmath.LegacyNewDec(100))), + ), + shouldErr: true, + }, + { + name: "invalid shares returns error", + entry: types.NewServiceDelegation( + 1, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdk.DecCoins{sdk.DecCoin{Denom: "umilk", Amount: sdkmath.LegacyNewDec(-100)}}, + ), + shouldErr: true, + }, + { + name: "valid entry returns no error", + entry: types.NewServiceDelegation( + 1, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdk.NewDecCoins(sdk.NewDecCoinFromDec("umilk", sdkmath.LegacyNewDec(100))), + ), + shouldErr: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := tc.entry.Validate() + if tc.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +// -------------------------------------------------------------------------------------------------------------------- + +func TestOperatorDelegation_Validate(t *testing.T) { + testCases := []struct { + name string + entry types.OperatorDelegation + shouldErr bool + }{ + { + name: "invalid operator id returns error", + entry: types.NewOperatorDelegation( + 0, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdk.NewDecCoins(sdk.NewDecCoinFromDec("umilk", sdkmath.LegacyNewDec(100))), + ), + shouldErr: true, + }, + { + name: "invalid user address returns error", + entry: types.NewOperatorDelegation( + 1, + "", + sdk.NewDecCoins(sdk.NewDecCoinFromDec("umilk", sdkmath.LegacyNewDec(100))), + ), + shouldErr: true, + }, + { + name: "invalid shares returns error", + entry: types.NewOperatorDelegation( + 1, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdk.DecCoins{sdk.DecCoin{Denom: "umilk", Amount: sdkmath.LegacyNewDec(-100)}}, + ), + shouldErr: true, + }, + { + name: "valid entry returns no error", + entry: types.NewOperatorDelegation( + 1, + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + sdk.NewDecCoins(sdk.NewDecCoinFromDec("umilk", sdkmath.LegacyNewDec(100))), + ), + shouldErr: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := tc.entry.Validate() + if tc.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/x/restaking/types/params.go b/x/restaking/types/params.go new file mode 100644 index 000000000..94edad9e3 --- /dev/null +++ b/x/restaking/types/params.go @@ -0,0 +1,27 @@ +package types + +import ( + "fmt" + "time" +) + +// NewParams returns a new Params instance +func NewParams(unbondingTime time.Duration) Params { + return Params{ + UnbondingTime: unbondingTime, + } +} + +// DefaultParams return a Params instance with default values set +func DefaultParams() Params { + return NewParams(3 * 24 * time.Hour) +} + +// Validate performs basic validation of params +func (p *Params) Validate() error { + if p.UnbondingTime == 0 { + return fmt.Errorf("invalid unbonding time: %s", p.UnbondingTime) + } + + return nil +} diff --git a/x/restaking/types/params.pb.go b/x/restaking/types/params.pb.go new file mode 100644 index 000000000..33270d737 --- /dev/null +++ b/x/restaking/types/params.pb.go @@ -0,0 +1,314 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: milkyway/restaking/v1/params.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + _ "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" + time "time" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf +var _ = time.Kitchen + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// Params defines the parameters for the module. +type Params struct { + // UnbondingTime represents the time that will take for assets to be unbonded + // after the user initiates an unbonding request. This will be applied to all + // types of restaking: pool, operator and service restaking. + UnbondingTime time.Duration `protobuf:"varint,1,opt,name=unbonding_time,json=unbondingTime,proto3,stdduration" json:"unbonding_time,omitempty"` +} + +func (m *Params) Reset() { *m = Params{} } +func (m *Params) String() string { return proto.CompactTextString(m) } +func (*Params) ProtoMessage() {} +func (*Params) Descriptor() ([]byte, []int) { + return fileDescriptor_342e630197fca2bb, []int{0} +} +func (m *Params) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Params.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Params) XXX_Merge(src proto.Message) { + xxx_messageInfo_Params.Merge(m, src) +} +func (m *Params) XXX_Size() int { + return m.Size() +} +func (m *Params) XXX_DiscardUnknown() { + xxx_messageInfo_Params.DiscardUnknown(m) +} + +var xxx_messageInfo_Params proto.InternalMessageInfo + +func (m *Params) GetUnbondingTime() time.Duration { + if m != nil { + return m.UnbondingTime + } + return 0 +} + +func init() { + proto.RegisterType((*Params)(nil), "milkyway.restaking.v1.Params") +} + +func init() { + proto.RegisterFile("milkyway/restaking/v1/params.proto", fileDescriptor_342e630197fca2bb) +} + +var fileDescriptor_342e630197fca2bb = []byte{ + // 229 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0xca, 0xcd, 0xcc, 0xc9, + 0xae, 0x2c, 0x4f, 0xac, 0xd4, 0x2f, 0x4a, 0x2d, 0x2e, 0x49, 0xcc, 0xce, 0xcc, 0x4b, 0xd7, 0x2f, + 0x33, 0xd4, 0x2f, 0x48, 0x2c, 0x4a, 0xcc, 0x2d, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, + 0x85, 0xa9, 0xd1, 0x83, 0xab, 0xd1, 0x2b, 0x33, 0x94, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0xab, + 0xd0, 0x07, 0xb1, 0x20, 0x8a, 0xa5, 0x24, 0x93, 0xf3, 0x8b, 0x73, 0xf3, 0x8b, 0xe3, 0x21, 0x12, + 0x10, 0x0e, 0x54, 0x4a, 0x0e, 0xc2, 0xd3, 0x4f, 0x4a, 0x2c, 0x4e, 0xd5, 0x2f, 0x33, 0x4c, 0x4a, + 0x2d, 0x49, 0x34, 0xd4, 0x4f, 0xce, 0xcf, 0xcc, 0x83, 0xc8, 0x2b, 0x99, 0x72, 0xb1, 0x05, 0x80, + 0xed, 0x15, 0xd2, 0xe6, 0xe2, 0x2b, 0xcd, 0x4b, 0xca, 0xcf, 0x4b, 0xc9, 0xcc, 0x4b, 0x8f, 0x2f, + 0xc9, 0xcc, 0x4d, 0x95, 0x60, 0x54, 0x60, 0xd4, 0x60, 0x76, 0x62, 0x99, 0x71, 0x5f, 0x9e, 0x31, + 0x88, 0x17, 0x2e, 0x17, 0x92, 0x99, 0x9b, 0xea, 0xe4, 0x7b, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, + 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, + 0xc7, 0x72, 0x0c, 0x51, 0xc6, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, + 0x30, 0x3f, 0xe8, 0xe6, 0x24, 0x26, 0x15, 0xc3, 0x79, 0xfa, 0x15, 0x48, 0xfe, 0x2e, 0xa9, 0x2c, + 0x48, 0x2d, 0x4e, 0x62, 0x03, 0x3b, 0xc6, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x28, 0xb9, 0x7d, + 0x57, 0x1a, 0x01, 0x00, 0x00, +} + +func (m *Params) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Params) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.UnbondingTime != 0 { + i = encodeVarintParams(dAtA, i, uint64(m.UnbondingTime)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintParams(dAtA []byte, offset int, v uint64) int { + offset -= sovParams(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Params) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.UnbondingTime != 0 { + n += 1 + sovParams(uint64(m.UnbondingTime)) + } + return n +} + +func sovParams(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozParams(x uint64) (n int) { + return sovParams(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Params) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Params: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UnbondingTime", wireType) + } + m.UnbondingTime = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.UnbondingTime |= time.Duration(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipParams(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthParams + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipParams(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowParams + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowParams + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowParams + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthParams + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupParams + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthParams + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthParams = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowParams = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupParams = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/restaking/types/params_test.go b/x/restaking/types/params_test.go new file mode 100644 index 000000000..e821977aa --- /dev/null +++ b/x/restaking/types/params_test.go @@ -0,0 +1,45 @@ +package types_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/milkyway-labs/milkyway/x/restaking/types" +) + +func TestParams_Validate(t *testing.T) { + testCases := []struct { + name string + params types.Params + shouldErr bool + }{ + { + name: "invalid unbonding time returns error", + params: types.NewParams(0), + shouldErr: true, + }, + { + name: "default params return no error", + params: types.DefaultParams(), + shouldErr: false, + }, + { + name: "valid params return no error", + params: types.NewParams(5 * time.Hour), + shouldErr: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := tc.params.Validate() + if tc.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/x/services/types/errors.go b/x/services/types/errors.go index 6e38d18e6..a2ab5ede4 100644 --- a/x/services/types/errors.go +++ b/x/services/types/errors.go @@ -9,4 +9,5 @@ var ( ErrServiceNotFound = errors.Register(ModuleName, 2, "service not found") ErrServiceAlreadyActive = errors.Register(ModuleName, 3, "service is already active") ErrServiceNotActive = errors.Register(ModuleName, 4, "service is not active") + ErrInsufficientShares = errors.Register(ModuleName, 5, "insufficient delegation shares") ) diff --git a/x/services/types/messages.pb.go b/x/services/types/messages.pb.go index 27434885f..d22bbd4ac 100644 --- a/x/services/types/messages.pb.go +++ b/x/services/types/messages.pb.go @@ -599,56 +599,55 @@ func init() { } var fileDescriptor_47bb30a8b53f4868 = []byte{ - // 771 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x56, 0xbf, 0x6f, 0xda, 0x4c, - 0x18, 0xc6, 0x09, 0xe1, 0x13, 0x47, 0xc8, 0x8f, 0x13, 0xfa, 0x42, 0xfc, 0xe5, 0xb3, 0xdb, 0x4b, - 0x5b, 0xa5, 0x34, 0xe0, 0x40, 0xa5, 0xa8, 0x62, 0x0b, 0x4d, 0x87, 0x56, 0xa1, 0x8a, 0x1c, 0x65, - 0xe9, 0x82, 0x0c, 0x3e, 0x39, 0x56, 0xb0, 0x8d, 0x7c, 0x06, 0xca, 0x56, 0x75, 0xec, 0xd4, 0x7f, - 0xa1, 0x43, 0xa5, 0x8e, 0x19, 0xfa, 0x1f, 0x74, 0xc9, 0x98, 0x76, 0xea, 0x64, 0x55, 0x64, 0xc8, - 0x5c, 0xfe, 0x82, 0x8a, 0xf3, 0xd9, 0x80, 0x81, 0x96, 0xb4, 0x6a, 0x16, 0xc4, 0xfb, 0xbe, 0xcf, - 0xdd, 0xf3, 0x3e, 0xf7, 0xdc, 0xbd, 0x00, 0x36, 0x0d, 0xbd, 0x7e, 0xda, 0x69, 0x2b, 0x1d, 0x89, - 0x60, 0xbb, 0xa5, 0xd7, 0x30, 0x91, 0x5a, 0x79, 0xc9, 0xc0, 0x84, 0x28, 0x1a, 0x26, 0xb9, 0x86, - 0x6d, 0x39, 0x16, 0x4c, 0xf9, 0xa0, 0x9c, 0x0f, 0xca, 0xb5, 0xf2, 0xfc, 0xaa, 0x62, 0xe8, 0xa6, - 0x25, 0xd1, 0x4f, 0x0f, 0xc8, 0xaf, 0xd7, 0x2c, 0x62, 0x58, 0xa4, 0x42, 0x23, 0xc9, 0x0b, 0x58, - 0x49, 0xf0, 0x22, 0xa9, 0xaa, 0x10, 0x2c, 0xb5, 0xf2, 0x55, 0xec, 0x28, 0x79, 0xa9, 0x66, 0xe9, - 0xe6, 0x58, 0xdd, 0x3c, 0x0d, 0xea, 0xfd, 0x80, 0xd5, 0xd7, 0x58, 0xdd, 0x20, 0x1a, 0xed, 0x90, - 0x68, 0xac, 0x90, 0xd2, 0x2c, 0xcd, 0xf2, 0x08, 0xfb, 0xdf, 0x58, 0xf6, 0xf6, 0x44, 0x5d, 0x0d, - 0xc5, 0x56, 0x0c, 0xd6, 0x11, 0xfa, 0x34, 0x07, 0x56, 0xca, 0x44, 0x7b, 0x6c, 0x63, 0xc5, 0xc1, - 0x47, 0x1e, 0x0a, 0xee, 0x80, 0x18, 0xc1, 0xa6, 0x8a, 0xed, 0x34, 0x77, 0x8b, 0xdb, 0x8a, 0x97, - 0xd2, 0x5f, 0x3e, 0x66, 0x53, 0x4c, 0xc8, 0x9e, 0xaa, 0xda, 0x98, 0x90, 0x23, 0xc7, 0xd6, 0x4d, - 0x4d, 0x66, 0x38, 0xb8, 0x09, 0xa2, 0xa6, 0x62, 0xe0, 0xf4, 0x1c, 0xc5, 0x2f, 0xf7, 0x5c, 0x31, - 0xd1, 0x51, 0x8c, 0x7a, 0x11, 0xf5, 0xb3, 0x48, 0xa6, 0x45, 0xf8, 0x08, 0x24, 0x54, 0x4c, 0x6a, - 0xb6, 0xde, 0x70, 0x74, 0xcb, 0x4c, 0xcf, 0x53, 0xec, 0xbf, 0x3d, 0x57, 0x84, 0x1e, 0x76, 0xa8, - 0x88, 0xe4, 0x61, 0x28, 0xdc, 0x06, 0xff, 0xb4, 0x71, 0x95, 0xe8, 0x0e, 0x4e, 0x47, 0xe9, 0x2a, - 0xd8, 0x73, 0xc5, 0x25, 0x6f, 0x15, 0x2b, 0x20, 0xd9, 0x87, 0xc0, 0x27, 0x20, 0xd1, 0xd0, 0x6b, - 0x4e, 0xd3, 0xc6, 0x95, 0xa6, 0x5d, 0x4f, 0x2f, 0xd0, 0x15, 0x77, 0xba, 0xae, 0x08, 0x0e, 0xbd, - 0xf4, 0xb1, 0x7c, 0x30, 0x60, 0x1d, 0x82, 0x22, 0x19, 0xb0, 0xe8, 0xd8, 0xae, 0x17, 0xef, 0xbf, - 0xbe, 0x3a, 0xcb, 0x30, 0x81, 0x6f, 0xae, 0xce, 0x32, 0xeb, 0xc1, 0x69, 0x86, 0x0f, 0x0c, 0xc9, - 0x20, 0x1d, 0xce, 0xc9, 0x98, 0x34, 0x2c, 0x93, 0x60, 0xb8, 0x0b, 0x96, 0x4c, 0xdc, 0xae, 0x30, - 0x07, 0x2a, 0xba, 0x4a, 0x0f, 0x35, 0x59, 0x5a, 0xe9, 0xba, 0xe2, 0xe2, 0x73, 0xdc, 0x66, 0xf8, - 0xa7, 0xfb, 0xf2, 0xa2, 0x39, 0x88, 0x54, 0xf4, 0xdd, 0x73, 0xe6, 0xb8, 0xa1, 0xfe, 0x91, 0x33, - 0xdb, 0x00, 0x0c, 0x51, 0xcf, 0x51, 0xea, 0x64, 0xd7, 0x15, 0xe3, 0x03, 0xde, 0x38, 0xf1, 0x49, - 0x03, 0x1f, 0xe7, 0xaf, 0xe1, 0x63, 0xf4, 0xb7, 0x7c, 0x5c, 0xb8, 0xb6, 0x8f, 0xb1, 0xbf, 0xe1, - 0xe3, 0xc8, 0xf1, 0x22, 0x9e, 0xfa, 0x38, 0x92, 0xf3, 0x7d, 0x44, 0xef, 0x38, 0x00, 0xcb, 0x44, - 0xdb, 0xab, 0x39, 0x7a, 0xeb, 0xe6, 0x1c, 0x29, 0x3e, 0x08, 0x75, 0xff, 0xdf, 0x70, 0xf7, 0xa1, - 0x66, 0xd0, 0x06, 0xe0, 0xc7, 0xb3, 0x81, 0x82, 0xf7, 0x1c, 0x48, 0x95, 0x89, 0xb6, 0x8f, 0x95, - 0x1b, 0xd6, 0x90, 0x0d, 0x69, 0xf8, 0x7f, 0x58, 0xc3, 0x58, 0x3b, 0x48, 0x00, 0x1b, 0x93, 0xf2, - 0x81, 0x8e, 0xcf, 0x1c, 0x58, 0x0e, 0x6c, 0x3a, 0xa4, 0xd3, 0x0c, 0x3e, 0x03, 0x71, 0xa5, 0xe9, - 0x9c, 0x58, 0xb6, 0xee, 0x74, 0x98, 0x8a, 0xed, 0x9e, 0x2b, 0xae, 0x78, 0x77, 0x23, 0x28, 0xa1, - 0xa9, 0xca, 0x06, 0xcb, 0x61, 0x11, 0xc4, 0xbc, 0x19, 0x49, 0x85, 0x25, 0x0a, 0x1b, 0xb9, 0x49, - 0xa3, 0x3f, 0xe7, 0x31, 0x97, 0xa2, 0xe7, 0xae, 0x18, 0x91, 0xd9, 0x8a, 0xe2, 0x6e, 0x5f, 0xea, - 0x60, 0xaf, 0xbe, 0xda, 0xc1, 0xaf, 0xcb, 0xcb, 0xc1, 0x1c, 0x0e, 0xf5, 0x8f, 0xd6, 0xc1, 0x5a, - 0x28, 0xe5, 0xcb, 0x2d, 0x7c, 0x88, 0x82, 0xf9, 0x32, 0xd1, 0xa0, 0x06, 0x92, 0xa3, 0x63, 0xfa, - 0xde, 0xe4, 0xbe, 0xc2, 0x93, 0x88, 0xcf, 0xcd, 0x86, 0x0b, 0x26, 0x96, 0x06, 0x92, 0xa3, 0x53, - 0x67, 0x3a, 0xd1, 0x08, 0xee, 0x27, 0x44, 0x13, 0x9f, 0x14, 0x34, 0xc0, 0x72, 0xf8, 0x39, 0x6d, - 0x4d, 0xdd, 0x22, 0x84, 0xe4, 0x77, 0x66, 0x45, 0x06, 0x74, 0x04, 0xac, 0x8e, 0xdf, 0xfd, 0xcc, - 0xd4, 0x6d, 0xc6, 0xb0, 0x7c, 0x61, 0x76, 0x6c, 0x40, 0xaa, 0x82, 0xc5, 0x91, 0x8b, 0x7a, 0xf7, - 0x17, 0x67, 0xe4, 0xc1, 0xf8, 0xec, 0x4c, 0x30, 0x9f, 0x85, 0x5f, 0x78, 0x75, 0x75, 0x96, 0xe1, - 0x4a, 0x07, 0xe7, 0x5d, 0x81, 0xbb, 0xe8, 0x0a, 0xdc, 0xb7, 0xae, 0xc0, 0xbd, 0xbd, 0x14, 0x22, - 0x17, 0x97, 0x42, 0xe4, 0xeb, 0xa5, 0x10, 0x79, 0x51, 0xd0, 0x74, 0xe7, 0xa4, 0x59, 0xcd, 0xd5, - 0x2c, 0x43, 0xf2, 0x77, 0xce, 0xd6, 0x95, 0x2a, 0x91, 0x26, 0xdd, 0x4e, 0xa7, 0xd3, 0xc0, 0xa4, - 0x1a, 0xa3, 0x7f, 0x11, 0x1e, 0xfe, 0x08, 0x00, 0x00, 0xff, 0xff, 0x4f, 0xc8, 0x35, 0x03, 0x1f, - 0x09, 0x00, 0x00, + // 768 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x56, 0x3f, 0x6f, 0xd3, 0x5e, + 0x14, 0x8d, 0xdb, 0x34, 0x3f, 0xe5, 0xa5, 0xe9, 0x1f, 0x2b, 0xfa, 0xd5, 0x35, 0xc5, 0x06, 0x17, + 0x50, 0x09, 0x4d, 0xdc, 0x04, 0x09, 0xa1, 0x6c, 0x0d, 0x65, 0x00, 0x35, 0xa8, 0x72, 0xd5, 0x85, + 0x25, 0x72, 0xe2, 0x27, 0xd7, 0x6a, 0xfc, 0x47, 0x7e, 0x4e, 0x42, 0x36, 0xc4, 0xc8, 0xc4, 0x57, + 0x60, 0x40, 0x62, 0xec, 0xc0, 0x37, 0x40, 0x42, 0x1d, 0x2b, 0x26, 0x26, 0x0b, 0xa5, 0x43, 0x67, + 0xf2, 0x09, 0x50, 0xde, 0x7b, 0x76, 0x12, 0x27, 0x81, 0x14, 0x44, 0x97, 0xaa, 0xf7, 0xde, 0x73, + 0xdf, 0xb9, 0xe7, 0x9d, 0x97, 0x2b, 0x83, 0x4d, 0xd3, 0x68, 0x9c, 0x74, 0xda, 0x6a, 0x47, 0x46, + 0xd0, 0x6d, 0x19, 0x75, 0x88, 0xe4, 0x56, 0x41, 0x36, 0x21, 0x42, 0xaa, 0x0e, 0x51, 0xde, 0x71, + 0x6d, 0xcf, 0x66, 0x33, 0x01, 0x28, 0x1f, 0x80, 0xf2, 0xad, 0x02, 0xbf, 0xaa, 0x9a, 0x86, 0x65, + 0xcb, 0xf8, 0x2f, 0x01, 0xf2, 0xeb, 0x75, 0x1b, 0x99, 0x36, 0xaa, 0xe2, 0x48, 0x26, 0x01, 0x2d, + 0x09, 0x24, 0x92, 0x6b, 0x2a, 0x82, 0x72, 0xab, 0x50, 0x83, 0x9e, 0x5a, 0x90, 0xeb, 0xb6, 0x61, + 0x8d, 0xd5, 0xad, 0x93, 0xb0, 0xde, 0x0f, 0x68, 0x7d, 0x8d, 0xd6, 0x4d, 0xa4, 0xe3, 0x09, 0x91, + 0x4e, 0x0b, 0x19, 0xdd, 0xd6, 0x6d, 0x42, 0xd8, 0xff, 0x8f, 0x66, 0x6f, 0x4f, 0xd4, 0xe5, 0xa8, + 0xae, 0x6a, 0xd2, 0x89, 0xa4, 0xcf, 0x73, 0x60, 0xa5, 0x82, 0xf4, 0x27, 0x2e, 0x54, 0x3d, 0x78, + 0x48, 0x50, 0xec, 0x0e, 0x48, 0x20, 0x68, 0x69, 0xd0, 0xe5, 0x98, 0x5b, 0xcc, 0x56, 0xb2, 0xcc, + 0x7d, 0xfd, 0x94, 0xcb, 0x50, 0x21, 0xbb, 0x9a, 0xe6, 0x42, 0x84, 0x0e, 0x3d, 0xd7, 0xb0, 0x74, + 0x85, 0xe2, 0xd8, 0x4d, 0x10, 0xb7, 0x54, 0x13, 0x72, 0x73, 0x18, 0xbf, 0xdc, 0xf3, 0xc5, 0x54, + 0x47, 0x35, 0x1b, 0x25, 0xa9, 0x9f, 0x95, 0x14, 0x5c, 0x64, 0x1f, 0x83, 0x94, 0x06, 0x51, 0xdd, + 0x35, 0x1c, 0xcf, 0xb0, 0x2d, 0x6e, 0x1e, 0x63, 0xff, 0xef, 0xf9, 0x22, 0x4b, 0xb0, 0x43, 0x45, + 0x49, 0x19, 0x86, 0xb2, 0xdb, 0xe0, 0xbf, 0x36, 0xac, 0x21, 0xc3, 0x83, 0x5c, 0x1c, 0x77, 0xb1, + 0x3d, 0x5f, 0x5c, 0x22, 0x5d, 0xb4, 0x20, 0x29, 0x01, 0x84, 0x7d, 0x0a, 0x52, 0x8e, 0x51, 0xf7, + 0x9a, 0x2e, 0xac, 0x36, 0xdd, 0x06, 0xb7, 0x80, 0x3b, 0xee, 0x74, 0x7d, 0x11, 0x1c, 0x90, 0xf4, + 0x91, 0xb2, 0x3f, 0x60, 0x1d, 0x82, 0x4a, 0x0a, 0xa0, 0xd1, 0x91, 0xdb, 0x28, 0xdd, 0x7f, 0x73, + 0x79, 0x9a, 0xa5, 0x02, 0xdf, 0x5e, 0x9e, 0x66, 0xd7, 0xc3, 0x4b, 0x8c, 0x5e, 0x98, 0xa4, 0x00, + 0x2e, 0x9a, 0x53, 0x20, 0x72, 0x6c, 0x0b, 0x41, 0xf6, 0x11, 0x58, 0xb2, 0x60, 0xbb, 0x4a, 0x9b, + 0xab, 0x86, 0x86, 0x2f, 0x35, 0x5d, 0x5e, 0xe9, 0xfa, 0xe2, 0xe2, 0x0b, 0xd8, 0xa6, 0xf8, 0x67, + 0x7b, 0xca, 0xa2, 0x35, 0x88, 0x34, 0xe9, 0x07, 0x71, 0xe6, 0xc8, 0xd1, 0xfe, 0xca, 0x99, 0x6d, + 0x00, 0x86, 0xa8, 0xe7, 0x30, 0x75, 0xba, 0xeb, 0x8b, 0xc9, 0x01, 0x6f, 0x12, 0x05, 0xa4, 0xa1, + 0x8f, 0xf3, 0x57, 0xf0, 0x31, 0xfe, 0x47, 0x3e, 0x2e, 0x5c, 0xd9, 0xc7, 0xc4, 0xbf, 0xf0, 0x71, + 0xe4, 0x7a, 0x25, 0x1e, 0xfb, 0x38, 0x92, 0x0b, 0x7c, 0x94, 0xde, 0x33, 0x80, 0xad, 0x20, 0x7d, + 0xb7, 0xee, 0x19, 0xad, 0xeb, 0x73, 0xa4, 0xf4, 0x20, 0x32, 0xfd, 0x8d, 0xe1, 0xe9, 0x23, 0xc3, + 0x48, 0x1b, 0x80, 0x1f, 0xcf, 0x86, 0x0a, 0x3e, 0x30, 0x20, 0x53, 0x41, 0xfa, 0x1e, 0x54, 0xaf, + 0x59, 0x43, 0x2e, 0xa2, 0xe1, 0xe6, 0xb0, 0x86, 0xb1, 0x71, 0x24, 0x01, 0x6c, 0x4c, 0xca, 0x87, + 0x3a, 0xbe, 0x30, 0x60, 0x39, 0xb4, 0xe9, 0x00, 0x6f, 0x33, 0xf6, 0x39, 0x48, 0xaa, 0x4d, 0xef, + 0xd8, 0x76, 0x0d, 0xaf, 0x43, 0x55, 0x6c, 0xf7, 0x7c, 0x71, 0x85, 0xbc, 0x8d, 0xb0, 0x24, 0x4d, + 0x55, 0x36, 0x68, 0x67, 0x4b, 0x20, 0x41, 0x76, 0x24, 0x16, 0x96, 0x2a, 0x6e, 0xe4, 0x27, 0xad, + 0xfe, 0x3c, 0x61, 0x2e, 0xc7, 0xcf, 0x7c, 0x31, 0xa6, 0xd0, 0x0e, 0x62, 0xd7, 0xe0, 0xac, 0xbe, + 0x5a, 0x6e, 0xfc, 0xbd, 0x91, 0x56, 0x69, 0x1d, 0xac, 0x45, 0x52, 0x81, 0xc6, 0xe2, 0xc7, 0x38, + 0x98, 0xaf, 0x20, 0x9d, 0xd5, 0x41, 0x7a, 0x74, 0x37, 0xdf, 0x9b, 0x3c, 0x4c, 0x74, 0xfd, 0xf0, + 0xf9, 0xd9, 0x70, 0xe1, 0x9a, 0xd2, 0x41, 0x7a, 0x74, 0xd5, 0x4c, 0x27, 0x1a, 0xc1, 0xfd, 0x82, + 0x68, 0xe2, 0xef, 0x88, 0x35, 0xc1, 0x72, 0xf4, 0x37, 0xb4, 0x35, 0xf5, 0x88, 0x08, 0x92, 0xdf, + 0x99, 0x15, 0x19, 0xd2, 0x21, 0xb0, 0x3a, 0xfe, 0xe0, 0xb3, 0x53, 0x8f, 0x19, 0xc3, 0xf2, 0xc5, + 0xd9, 0xb1, 0x21, 0xa9, 0x06, 0x16, 0x47, 0x5e, 0xe7, 0xdd, 0xdf, 0xdc, 0x11, 0x81, 0xf1, 0xb9, + 0x99, 0x60, 0x01, 0x0b, 0xbf, 0xf0, 0xfa, 0xf2, 0x34, 0xcb, 0x94, 0xf7, 0xcf, 0xba, 0x02, 0x73, + 0xde, 0x15, 0x98, 0xef, 0x5d, 0x81, 0x79, 0x77, 0x21, 0xc4, 0xce, 0x2f, 0x84, 0xd8, 0xb7, 0x0b, + 0x21, 0xf6, 0xb2, 0xa8, 0x1b, 0xde, 0x71, 0xb3, 0x96, 0xaf, 0xdb, 0xa6, 0x1c, 0x9c, 0x9c, 0x6b, + 0xa8, 0x35, 0x14, 0x46, 0xf2, 0xab, 0xc1, 0xa7, 0x81, 0xd7, 0x71, 0x20, 0xaa, 0x25, 0xf0, 0x77, + 0xc1, 0xc3, 0x9f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xa5, 0x1c, 0x08, 0x5f, 0x14, 0x09, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/x/services/types/models.go b/x/services/types/models.go index a51ff89c6..59d5ef7d8 100644 --- a/x/services/types/models.go +++ b/x/services/types/models.go @@ -5,6 +5,7 @@ import ( "strconv" "strings" + sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" ) @@ -48,25 +49,25 @@ func NewService( } // Validate checks that the Service has valid values. -func (a *Service) Validate() error { - if a.Status == SERVICE_STATUS_UNSPECIFIED { - return fmt.Errorf("invalid status: %s", a.Status) +func (s Service) Validate() error { + if s.Status == SERVICE_STATUS_UNSPECIFIED { + return fmt.Errorf("invalid status: %s", s.Status) } - if a.ID == 0 { - return fmt.Errorf("invalid id: %d", a.ID) + if s.ID == 0 { + return fmt.Errorf("invalid id: %d", s.ID) } - if strings.TrimSpace(a.Name) == "" { - return fmt.Errorf("invalid name: %s", a.Name) + if strings.TrimSpace(s.Name) == "" { + return fmt.Errorf("invalid name: %s", s.Name) } - _, err := sdk.AccAddressFromBech32(a.Admin) + _, err := sdk.AccAddressFromBech32(s.Admin) if err != nil { return fmt.Errorf("invalid admin address") } - _, err = sdk.AccAddressFromBech32(a.Address) + _, err = sdk.AccAddressFromBech32(s.Address) if err != nil { return fmt.Errorf("invalid service address") } @@ -74,6 +75,73 @@ func (a *Service) Validate() error { return nil } +// GetSharesDenom returns the shares denom for a service and token denom +func (s Service) GetSharesDenom(tokenDenom string) string { + return fmt.Sprintf("service/%d/%s", s.ID, tokenDenom) +} + +// IsActive returns whether the service is active. +func (s Service) IsActive() bool { + return s.Status == SERVICE_STATUS_ACTIVE +} + +// InvalidExRate returns whether the exchange rates is invalid. +// This can happen e.g. if Service loses all tokens due to slashing. In this case, +// make all future delegations invalid. +func (s Service) InvalidExRate() bool { + for _, token := range s.Tokens { + if token.IsZero() && s.DelegatorShares.AmountOf(token.Denom).IsPositive() { + return true + } + } + return false +} + +// SharesFromTokens returns the shares of a delegation given a bond amount. It +// returns an error if the service has no tokens. +func (s Service) SharesFromTokens(tokens sdk.Coin) (sdkmath.LegacyDec, error) { + if s.Tokens.IsZero() { + return sdkmath.LegacyZeroDec(), ErrInsufficientShares + } + + sharesDenom := s.GetSharesDenom(tokens.Denom) + delegatorTokenShares := s.DelegatorShares.AmountOf(sharesDenom) + operatorTokenAmount := s.Tokens.AmountOf(tokens.Denom) + + return delegatorTokenShares.MulInt(tokens.Amount).QuoInt(operatorTokenAmount), nil +} + +// AddTokensFromDelegation adds the given amount of tokens to the service's total tokens, +// also updating the service's delegator shares. +// It returns the updated service and the shares issued. +func (s Service) AddTokensFromDelegation(amount sdk.Coins) (Service, sdk.DecCoins) { + // calculate the shares to issue + issuedShares := sdk.NewDecCoins() + for _, token := range amount { + var tokenShares sdk.DecCoin + sharesDenom := s.GetSharesDenom(token.Denom) + + delegatorShares := s.DelegatorShares.AmountOf(sharesDenom) + if delegatorShares.IsZero() { + // The first delegation to an operator sets the exchange rate to one + tokenShares = sdk.NewDecCoin(sharesDenom, token.Amount) + } else { + shares, err := s.SharesFromTokens(token) + if err != nil { + panic(err) + } + tokenShares = sdk.NewDecCoinFromDec(sharesDenom, shares) + } + + issuedShares = issuedShares.Add(tokenShares) + } + + s.Tokens = s.Tokens.Add(amount...) + s.DelegatorShares = s.DelegatorShares.Add(issuedShares...) + + return s, issuedShares +} + // -------------------------------------------------------------------------------------------------------------------- // ServiceUpdate defines the fields that can be updated in a Service. diff --git a/x/services/types/models.pb.go b/x/services/types/models.pb.go index 72714dfec..bbe3a3290 100644 --- a/x/services/types/models.pb.go +++ b/x/services/types/models.pb.go @@ -6,6 +6,8 @@ package types import ( fmt "fmt" _ "github.com/cosmos/cosmos-proto" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" io "io" @@ -77,11 +79,15 @@ type Service struct { // Website is the website of the service Website string `protobuf:"bytes,6,opt,name=website,proto3" json:"website,omitempty"` // PictureURL is the URL of the picture of the service - PictureURL string `protobuf:"bytes,7,opt,name=pictureURL,proto3" json:"pictureURL,omitempty"` + PictureURL string `protobuf:"bytes,7,opt,name=picture_url,json=pictureUrl,proto3" json:"picture_url,omitempty"` // Address is the address of the account associated with the service. // This will be used in order to store all the tokens that are delegated to // this service by various users. Address string `protobuf:"bytes,8,opt,name=address,proto3" json:"address,omitempty"` + // Tokens define the delegated tokens. + Tokens github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,9,rep,name=tokens,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"tokens"` + // DelegatorShares define the total shares issued to an operator's delegators. + DelegatorShares github_com_cosmos_cosmos_sdk_types.DecCoins `protobuf:"bytes,10,rep,name=delegator_shares,json=delegatorShares,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.DecCoins" json:"delegator_shares"` } func (m *Service) Reset() { *m = Service{} } @@ -173,6 +179,20 @@ func (m *Service) GetAddress() string { return "" } +func (m *Service) GetTokens() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.Tokens + } + return nil +} + +func (m *Service) GetDelegatorShares() github_com_cosmos_cosmos_sdk_types.DecCoins { + if m != nil { + return m.DelegatorShares + } + return nil +} + func init() { proto.RegisterEnum("milkyway.services.v1.ServiceStatus", ServiceStatus_name, ServiceStatus_value) proto.RegisterType((*Service)(nil), "milkyway.services.v1.Service") @@ -181,34 +201,42 @@ func init() { func init() { proto.RegisterFile("milkyway/services/v1/models.proto", fileDescriptor_4411e719afee9a70) } var fileDescriptor_4411e719afee9a70 = []byte{ - // 426 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x92, 0xcd, 0x6e, 0xd3, 0x40, - 0x10, 0x80, 0xbd, 0x6e, 0x9a, 0xc0, 0xa0, 0xa2, 0x68, 0x15, 0xca, 0xd6, 0x48, 0x4b, 0x80, 0x4b, - 0x84, 0x54, 0x5b, 0x0d, 0x47, 0x4e, 0xf9, 0x31, 0x92, 0xa5, 0xaa, 0x42, 0x76, 0xd2, 0x03, 0x97, - 0xc8, 0x3f, 0x2b, 0xb3, 0x22, 0xf6, 0x5a, 0xde, 0x4d, 0x4a, 0xde, 0x00, 0x6e, 0xbc, 0x03, 0x6f, - 0x80, 0x78, 0x08, 0x8e, 0x15, 0x27, 0x4e, 0x08, 0x39, 0x2f, 0x82, 0xea, 0x1f, 0x08, 0x11, 0x52, - 0x6f, 0x33, 0xf3, 0x7d, 0x3b, 0x3b, 0xab, 0x1d, 0x78, 0x92, 0xf0, 0xe5, 0xbb, 0xcd, 0x95, 0xbf, - 0xb1, 0x24, 0xcb, 0xd7, 0x3c, 0x64, 0xd2, 0x5a, 0x9f, 0x59, 0x89, 0x88, 0xd8, 0x52, 0x9a, 0x59, - 0x2e, 0x94, 0xc0, 0xbd, 0x46, 0x31, 0x1b, 0xc5, 0x5c, 0x9f, 0x19, 0xbd, 0x58, 0xc4, 0xa2, 0x14, - 0xac, 0x9b, 0xa8, 0x72, 0x8d, 0x93, 0x50, 0xc8, 0x44, 0xc8, 0x45, 0x05, 0xaa, 0xa4, 0x42, 0x4f, - 0xbf, 0xe8, 0xd0, 0xf1, 0xaa, 0x06, 0xf8, 0x18, 0x74, 0x1e, 0x11, 0xd4, 0x47, 0x83, 0xa3, 0x71, - 0xbb, 0xf8, 0xf9, 0x58, 0x77, 0xa6, 0xae, 0xce, 0x23, 0xfc, 0x12, 0xda, 0x52, 0xf9, 0x6a, 0x25, - 0x89, 0xde, 0x47, 0x83, 0xfb, 0xc3, 0x67, 0xe6, 0xff, 0xee, 0x36, 0xeb, 0x36, 0x5e, 0xa9, 0xba, - 0xf5, 0x11, 0x6c, 0xc2, 0xa1, 0x1f, 0x25, 0x3c, 0x25, 0x07, 0x7d, 0x34, 0xb8, 0x3b, 0x26, 0xdf, - 0xbf, 0x9e, 0xf6, 0xea, 0x09, 0x46, 0x51, 0x94, 0x33, 0x29, 0x3d, 0x95, 0xf3, 0x34, 0x76, 0x2b, - 0x0d, 0x63, 0x68, 0xa5, 0x7e, 0xc2, 0x48, 0xeb, 0x46, 0x77, 0xcb, 0x18, 0xf7, 0xe1, 0x5e, 0xc4, - 0x64, 0x98, 0xf3, 0x4c, 0x71, 0x91, 0x92, 0xc3, 0x12, 0xed, 0x96, 0x30, 0x81, 0xce, 0x15, 0x0b, - 0x24, 0x57, 0x8c, 0xb4, 0x4b, 0xda, 0xa4, 0x98, 0x02, 0x64, 0x3c, 0x54, 0xab, 0x9c, 0xcd, 0xdd, - 0x73, 0xd2, 0x29, 0xe1, 0x4e, 0x05, 0x0f, 0xa1, 0xe3, 0x57, 0x73, 0x90, 0x3b, 0xb7, 0x4c, 0xd8, - 0x88, 0xcf, 0x3f, 0x22, 0x38, 0xfa, 0xe7, 0xb5, 0x98, 0x82, 0xe1, 0xd9, 0xee, 0xa5, 0x33, 0xb1, - 0x17, 0xde, 0x6c, 0x34, 0x9b, 0x7b, 0x8b, 0xf9, 0x85, 0xf7, 0xda, 0x9e, 0x38, 0xaf, 0x1c, 0x7b, - 0xda, 0xd5, 0xb0, 0x01, 0xc7, 0x7b, 0x7c, 0xe2, 0xda, 0xa3, 0x99, 0x3d, 0xed, 0x22, 0x7c, 0x02, - 0x0f, 0xf6, 0xd8, 0x68, 0x32, 0x73, 0x2e, 0xed, 0xae, 0x8e, 0x1f, 0xc1, 0xc3, 0x3d, 0xe4, 0x5c, - 0xd4, 0xf0, 0xc0, 0x68, 0x7d, 0xf8, 0x4c, 0xb5, 0xf1, 0xf9, 0xb7, 0x82, 0xa2, 0xeb, 0x82, 0xa2, - 0x5f, 0x05, 0x45, 0x9f, 0xb6, 0x54, 0xbb, 0xde, 0x52, 0xed, 0xc7, 0x96, 0x6a, 0x6f, 0x86, 0x31, - 0x57, 0x6f, 0x57, 0x81, 0x19, 0x8a, 0xc4, 0x6a, 0x3e, 0xec, 0x74, 0xe9, 0x07, 0xf2, 0x4f, 0x66, - 0xbd, 0xff, 0xbb, 0x5f, 0x6a, 0x93, 0x31, 0x19, 0xb4, 0xcb, 0xad, 0x78, 0xf1, 0x3b, 0x00, 0x00, - 0xff, 0xff, 0x40, 0x5a, 0x2a, 0x1e, 0x81, 0x02, 0x00, 0x00, + // 556 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0x4d, 0x6f, 0xd3, 0x30, + 0x1c, 0xc6, 0x93, 0xb6, 0x6b, 0x99, 0xab, 0x8d, 0xca, 0x2a, 0xc3, 0x2d, 0x28, 0x2d, 0x70, 0xa9, + 0x40, 0x4d, 0x68, 0x77, 0xe4, 0xd4, 0x97, 0x20, 0x45, 0x9a, 0xa6, 0x29, 0x69, 0x77, 0xe0, 0x52, + 0xe5, 0xc5, 0xea, 0xac, 0x26, 0x71, 0x15, 0xbb, 0x1d, 0x95, 0xf8, 0x00, 0x70, 0xe3, 0x3b, 0x20, + 0x71, 0xe0, 0xcc, 0x87, 0xd8, 0x71, 0xe2, 0xc4, 0xa9, 0xa0, 0xf6, 0x8b, 0xa0, 0x3a, 0xee, 0x18, + 0xd5, 0x24, 0x76, 0x8a, 0xff, 0x7e, 0x7e, 0x7f, 0x3f, 0x8f, 0xed, 0x18, 0x3c, 0x8b, 0x48, 0x38, + 0x59, 0x5c, 0xba, 0x0b, 0x83, 0xe1, 0x64, 0x4e, 0x7c, 0xcc, 0x8c, 0x79, 0xcb, 0x88, 0x68, 0x80, + 0x43, 0xa6, 0x4f, 0x13, 0xca, 0x29, 0x2c, 0x6f, 0x11, 0x7d, 0x8b, 0xe8, 0xf3, 0x56, 0xb5, 0x3c, + 0xa6, 0x63, 0x2a, 0x00, 0x63, 0x33, 0x4a, 0xd9, 0x6a, 0xc5, 0xa7, 0x2c, 0xa2, 0x6c, 0x94, 0x0a, + 0x69, 0x21, 0x25, 0x2d, 0xad, 0x0c, 0xcf, 0x65, 0xd8, 0x98, 0xb7, 0x3c, 0xcc, 0xdd, 0x96, 0xe1, + 0x53, 0x12, 0xa7, 0xfa, 0xf3, 0xaf, 0x39, 0x50, 0x70, 0x52, 0x03, 0x78, 0x04, 0x32, 0x24, 0x40, + 0x6a, 0x5d, 0x6d, 0x1c, 0x74, 0xf3, 0xab, 0x65, 0x2d, 0x63, 0xf5, 0xed, 0x0c, 0x09, 0xe0, 0x1b, + 0x90, 0x67, 0xdc, 0xe5, 0x33, 0x86, 0x32, 0x75, 0xb5, 0x71, 0xd8, 0x7e, 0xa1, 0xdf, 0x95, 0x4d, + 0x97, 0xcb, 0x38, 0x02, 0xb5, 0x65, 0x0b, 0xd4, 0xc1, 0x9e, 0x1b, 0x44, 0x24, 0x46, 0xd9, 0xba, + 0xda, 0xd8, 0xef, 0xa2, 0x1f, 0xdf, 0x9b, 0x65, 0x99, 0xb0, 0x13, 0x04, 0x09, 0x66, 0xcc, 0xe1, + 0x09, 0x89, 0xc7, 0x76, 0x8a, 0x41, 0x08, 0x72, 0xb1, 0x1b, 0x61, 0x94, 0xdb, 0xe0, 0xb6, 0x18, + 0xc3, 0x3a, 0x28, 0x06, 0x98, 0xf9, 0x09, 0x99, 0x72, 0x42, 0x63, 0xb4, 0x27, 0xa4, 0xdb, 0x53, + 0x10, 0x81, 0xc2, 0x25, 0xf6, 0x18, 0xe1, 0x18, 0xe5, 0x85, 0xba, 0x2d, 0xa1, 0x01, 0x8a, 0x53, + 0xe2, 0xf3, 0x59, 0x82, 0x47, 0xb3, 0x24, 0x44, 0x05, 0x91, 0xe2, 0x70, 0xb5, 0xac, 0x81, 0xb3, + 0x74, 0x7a, 0x68, 0x9f, 0xd8, 0x40, 0x22, 0xc3, 0x24, 0x84, 0x6d, 0x50, 0x70, 0xd3, 0x60, 0xe8, + 0xc1, 0x7f, 0x22, 0x6f, 0x41, 0xe8, 0x83, 0x3c, 0xa7, 0x13, 0x1c, 0x33, 0xb4, 0x5f, 0xcf, 0x36, + 0x8a, 0xed, 0x8a, 0x2e, 0xf9, 0xcd, 0xb1, 0xeb, 0xf2, 0xd8, 0xf5, 0x1e, 0x25, 0x71, 0xf7, 0xf5, + 0xd5, 0xb2, 0xa6, 0x7c, 0xfb, 0x55, 0x6b, 0x8c, 0x09, 0xbf, 0x98, 0x79, 0xba, 0x4f, 0x23, 0x79, + 0x63, 0xf2, 0xd3, 0x64, 0xc1, 0xc4, 0xe0, 0x8b, 0x29, 0x66, 0xa2, 0x81, 0xd9, 0x72, 0x69, 0xf8, + 0x01, 0x94, 0x02, 0x1c, 0xe2, 0xb1, 0xcb, 0x69, 0x32, 0x62, 0x17, 0x6e, 0x82, 0x19, 0x02, 0xc2, + 0xee, 0xe9, 0x9d, 0x76, 0x7d, 0xec, 0x0b, 0xc7, 0x63, 0xe9, 0xf8, 0xea, 0x1e, 0x8e, 0xb2, 0x87, + 0xd9, 0x0f, 0x6f, 0xac, 0x1c, 0xe1, 0xf4, 0xf2, 0x93, 0x0a, 0x0e, 0xfe, 0xb9, 0x61, 0xa8, 0x81, + 0xaa, 0x63, 0xda, 0xe7, 0x56, 0xcf, 0x1c, 0x39, 0x83, 0xce, 0x60, 0xe8, 0x8c, 0x86, 0xa7, 0xce, + 0x99, 0xd9, 0xb3, 0xde, 0x5a, 0x66, 0xbf, 0xa4, 0xc0, 0x2a, 0x38, 0xda, 0xd1, 0x7b, 0xb6, 0xd9, + 0x19, 0x98, 0xfd, 0x92, 0x0a, 0x2b, 0xe0, 0xd1, 0x8e, 0xd6, 0xe9, 0x0d, 0xac, 0x73, 0xb3, 0x94, + 0x81, 0x4f, 0xc0, 0xe3, 0x1d, 0xc9, 0x3a, 0x95, 0x62, 0xb6, 0x9a, 0xfb, 0xf8, 0x45, 0x53, 0xba, + 0x27, 0x57, 0x2b, 0x4d, 0xbd, 0x5e, 0x69, 0xea, 0xef, 0x95, 0xa6, 0x7e, 0x5e, 0x6b, 0xca, 0xf5, + 0x5a, 0x53, 0x7e, 0xae, 0x35, 0xe5, 0x5d, 0xfb, 0xd6, 0x1e, 0xb7, 0x3f, 0x69, 0x33, 0x74, 0x3d, + 0x76, 0x53, 0x19, 0xef, 0xff, 0xbe, 0x39, 0xb1, 0x67, 0x2f, 0x2f, 0x5e, 0xc2, 0xf1, 0x9f, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x10, 0xa5, 0x37, 0xb3, 0x95, 0x03, 0x00, 0x00, } func (m *Service) Marshal() (dAtA []byte, err error) { @@ -231,6 +259,34 @@ func (m *Service) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.DelegatorShares) > 0 { + for iNdEx := len(m.DelegatorShares) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.DelegatorShares[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintModels(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x52 + } + } + if len(m.Tokens) > 0 { + for iNdEx := len(m.Tokens) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Tokens[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintModels(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + } if len(m.Address) > 0 { i -= len(m.Address) copy(dAtA[i:], m.Address) @@ -333,6 +389,18 @@ func (m *Service) Size() (n int) { if l > 0 { n += 1 + l + sovModels(uint64(l)) } + if len(m.Tokens) > 0 { + for _, e := range m.Tokens { + l = e.Size() + n += 1 + l + sovModels(uint64(l)) + } + } + if len(m.DelegatorShares) > 0 { + for _, e := range m.DelegatorShares { + l = e.Size() + n += 1 + l + sovModels(uint64(l)) + } + } return n } @@ -601,6 +669,74 @@ func (m *Service) Unmarshal(dAtA []byte) error { } m.Address = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Tokens", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthModels + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthModels + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Tokens = append(m.Tokens, types.Coin{}) + if err := m.Tokens[len(m.Tokens)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorShares", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthModels + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthModels + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DelegatorShares = append(m.DelegatorShares, types.DecCoin{}) + if err := m.DelegatorShares[len(m.DelegatorShares)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipModels(dAtA[iNdEx:])