diff --git a/docs/static/openapi.yml b/docs/static/openapi.yml index d9ab35d..bfc1c61 100644 --- a/docs/static/openapi.yml +++ b/docs/static/openapi.yml @@ -1 +1 @@ -{"id":"topchain","consumes":["application/json"],"produces":["application/json"],"swagger":"2.0","info":{"description":"Chain topchain REST API","title":"HTTP API Console","contact":{"name":"topchain"},"version":"version not set"},"paths":{"/topchain.subscription.Msg/CancelDeal":{"post":{"tags":["Msg"],"operationId":"TopchainMsg_CancelDeal","parameters":[{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/topchain.subscription.MsgCancelDeal"}}],"responses":{"200":{"description":"A successful response.","schema":{"$ref":"#/definitions/topchain.subscription.MsgCancelDealResponse"}},"default":{"description":"An unexpected error response.","schema":{"$ref":"#/definitions/google.rpc.Status"}}}}},"/topchain.subscription.Msg/CreateDeal":{"post":{"tags":["Msg"],"operationId":"TopchainMsg_CreateDeal","parameters":[{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/topchain.subscription.MsgCreateDeal"}}],"responses":{"200":{"description":"A successful response.","schema":{"$ref":"#/definitions/topchain.subscription.MsgCreateDealResponse"}},"default":{"description":"An unexpected error response.","schema":{"$ref":"#/definitions/google.rpc.Status"}}}}},"/topchain.subscription.Msg/JoinDeal":{"post":{"tags":["Msg"],"operationId":"TopchainMsg_JoinDeal","parameters":[{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/topchain.subscription.MsgJoinDeal"}}],"responses":{"200":{"description":"A successful response.","schema":{"$ref":"#/definitions/topchain.subscription.MsgJoinDealResponse"}},"default":{"description":"An unexpected error response.","schema":{"$ref":"#/definitions/google.rpc.Status"}}}}},"/topchain.subscription.Msg/LeaveDeal":{"post":{"tags":["Msg"],"operationId":"TopchainMsg_LeaveDeal","parameters":[{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/topchain.subscription.MsgLeaveDeal"}}],"responses":{"200":{"description":"A successful response.","schema":{"$ref":"#/definitions/topchain.subscription.MsgLeaveDealResponse"}},"default":{"description":"An unexpected error response.","schema":{"$ref":"#/definitions/google.rpc.Status"}}}}},"/topchain.subscription.Msg/UpdateDeal":{"post":{"tags":["Msg"],"operationId":"TopchainMsg_UpdateDeal","parameters":[{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/topchain.subscription.MsgUpdateDeal"}}],"responses":{"200":{"description":"A successful response.","schema":{"$ref":"#/definitions/topchain.subscription.MsgUpdateDealResponse"}},"default":{"description":"An unexpected error response.","schema":{"$ref":"#/definitions/google.rpc.Status"}}}}},"/topchain.subscription.Msg/UpdateParams":{"post":{"tags":["Msg"],"summary":"UpdateParams defines a (governance) operation for updating the module\nparameters. The authority defaults to the x/gov module account.","operationId":"TopchainMsg_UpdateParams","parameters":[{"description":"MsgUpdateParams is the Msg/UpdateParams request type.","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/topchain.subscription.MsgUpdateParams"}}],"responses":{"200":{"description":"A successful response.","schema":{"$ref":"#/definitions/topchain.subscription.MsgUpdateParamsResponse"}},"default":{"description":"An unexpected error response.","schema":{"$ref":"#/definitions/google.rpc.Status"}}}}},"/topchain/subscription/deal/{id}":{"get":{"tags":["Query"],"operationId":"TopchainQuery_Deal","parameters":[{"type":"string","name":"id","in":"path","required":true}],"responses":{"200":{"description":"A successful response.","schema":{"$ref":"#/definitions/topchain.subscription.QueryDealResponse"}},"default":{"description":"An unexpected error response.","schema":{"$ref":"#/definitions/google.rpc.Status"}}}}},"/topchain/subscription/deal_status/{id}":{"get":{"tags":["Query"],"operationId":"TopchainQuery_DealStatus","parameters":[{"type":"string","name":"id","in":"path","required":true}],"responses":{"200":{"description":"A successful response.","schema":{"$ref":"#/definitions/topchain.subscription.QueryDealStatusResponse"}},"default":{"description":"An unexpected error response.","schema":{"$ref":"#/definitions/google.rpc.Status"}}}}},"/topchain/subscription/deals/{requester}":{"get":{"tags":["Query"],"operationId":"TopchainQuery_Deals","parameters":[{"type":"string","name":"requester","in":"path","required":true},{"type":"string","format":"byte","description":"key is a value returned in PageResponse.next_key to begin\nquerying the next page most efficiently. Only one of offset or key\nshould be set.","name":"pagination.key","in":"query"},{"type":"string","format":"uint64","description":"offset is a numeric offset that can be used when key is unavailable.\nIt is less efficient than using key. Only one of offset or key should\nbe set.","name":"pagination.offset","in":"query"},{"type":"string","format":"uint64","description":"limit is the total number of results to be returned in the result page.\nIf left empty it will default to a value to be set by each app.","name":"pagination.limit","in":"query"},{"type":"boolean","description":"count_total is set to true to indicate that the result set should include\na count of the total number of items available for pagination in UIs.\ncount_total is only respected when offset is used. It is ignored when key\nis set.","name":"pagination.count_total","in":"query"},{"type":"boolean","description":"reverse is set to true if results are to be returned in the descending order.\n\nSince: cosmos-sdk 0.43","name":"pagination.reverse","in":"query"}],"responses":{"200":{"description":"A successful response.","schema":{"$ref":"#/definitions/topchain.subscription.QueryDealsResponse"}},"default":{"description":"An unexpected error response.","schema":{"$ref":"#/definitions/google.rpc.Status"}}}}},"/topchain/subscription/params":{"get":{"tags":["Query"],"summary":"Parameters queries the parameters of the module.","operationId":"TopchainQuery_Params","responses":{"200":{"description":"A successful response.","schema":{"$ref":"#/definitions/topchain.subscription.QueryParamsResponse"}},"default":{"description":"An unexpected error response.","schema":{"$ref":"#/definitions/google.rpc.Status"}}}}},"/topchain/subscription/subscription/{id}":{"get":{"tags":["Query"],"operationId":"TopchainQuery_Subscription","parameters":[{"type":"string","name":"id","in":"path","required":true}],"responses":{"200":{"description":"A successful response.","schema":{"$ref":"#/definitions/topchain.subscription.QuerySubscriptionResponse"}},"default":{"description":"An unexpected error response.","schema":{"$ref":"#/definitions/google.rpc.Status"}}}}},"/topchain/subscription/subscriptions/{provider}":{"get":{"tags":["Query"],"operationId":"TopchainQuery_Subscriptions","parameters":[{"type":"string","name":"provider","in":"path","required":true},{"type":"string","format":"byte","description":"key is a value returned in PageResponse.next_key to begin\nquerying the next page most efficiently. Only one of offset or key\nshould be set.","name":"pagination.key","in":"query"},{"type":"string","format":"uint64","description":"offset is a numeric offset that can be used when key is unavailable.\nIt is less efficient than using key. Only one of offset or key should\nbe set.","name":"pagination.offset","in":"query"},{"type":"string","format":"uint64","description":"limit is the total number of results to be returned in the result page.\nIf left empty it will default to a value to be set by each app.","name":"pagination.limit","in":"query"},{"type":"boolean","description":"count_total is set to true to indicate that the result set should include\na count of the total number of items available for pagination in UIs.\ncount_total is only respected when offset is used. It is ignored when key\nis set.","name":"pagination.count_total","in":"query"},{"type":"boolean","description":"reverse is set to true if results are to be returned in the descending order.\n\nSince: cosmos-sdk 0.43","name":"pagination.reverse","in":"query"}],"responses":{"200":{"description":"A successful response.","schema":{"$ref":"#/definitions/topchain.subscription.QuerySubscriptionsResponse"}},"default":{"description":"An unexpected error response.","schema":{"$ref":"#/definitions/google.rpc.Status"}}}}}},"definitions":{"cosmos.base.query.v1beta1.PageRequest":{"description":"message SomeRequest {\n Foo some_parameter = 1;\n PageRequest pagination = 2;\n }","type":"object","title":"PageRequest is to be embedded in gRPC request messages for efficient\npagination. Ex:","properties":{"count_total":{"description":"count_total is set to true to indicate that the result set should include\na count of the total number of items available for pagination in UIs.\ncount_total is only respected when offset is used. It is ignored when key\nis set.","type":"boolean"},"key":{"description":"key is a value returned in PageResponse.next_key to begin\nquerying the next page most efficiently. Only one of offset or key\nshould be set.","type":"string","format":"byte"},"limit":{"description":"limit is the total number of results to be returned in the result page.\nIf left empty it will default to a value to be set by each app.","type":"string","format":"uint64"},"offset":{"description":"offset is a numeric offset that can be used when key is unavailable.\nIt is less efficient than using key. Only one of offset or key should\nbe set.","type":"string","format":"uint64"},"reverse":{"description":"reverse is set to true if results are to be returned in the descending order.\n\nSince: cosmos-sdk 0.43","type":"boolean"}}},"cosmos.base.query.v1beta1.PageResponse":{"description":"PageResponse is to be embedded in gRPC response messages where the\ncorresponding request message has used PageRequest.\n\n message SomeResponse {\n repeated Bar results = 1;\n PageResponse page = 2;\n }","type":"object","properties":{"next_key":{"description":"next_key is the key to be passed to PageRequest.key to\nquery the next page most efficiently. It will be empty if\nthere are no more results.","type":"string","format":"byte"},"total":{"type":"string","format":"uint64","title":"total is total number of results available if PageRequest.count_total\nwas set, its value is undefined otherwise"}}},"google.protobuf.Any":{"type":"object","properties":{"@type":{"type":"string"}},"additionalProperties":{}},"google.rpc.Status":{"type":"object","properties":{"code":{"type":"integer","format":"int32"},"details":{"type":"array","items":{"type":"object","$ref":"#/definitions/google.protobuf.Any"}},"message":{"type":"string"}}},"topchain.subscription.Deal":{"type":"object","properties":{"available_amount":{"type":"string","format":"uint64"},"cro_id":{"type":"string"},"end_block":{"type":"string","format":"uint64"},"id":{"type":"string"},"initial_amount":{"type":"string","format":"uint64"},"requester":{"type":"string"},"start_block":{"type":"string","format":"uint64"},"status":{"$ref":"#/definitions/topchain.subscription.Deal.Status"},"subscription_ids":{"type":"array","items":{"type":"string"}}}},"topchain.subscription.Deal.Status":{"type":"string","default":"SCHEDULED","enum":["SCHEDULED","INITIALIZED","ACTIVE","INACTIVE","CANCELLED","EXPIRED"]},"topchain.subscription.MsgCancelDeal":{"type":"object","properties":{"deal_id":{"type":"string"},"requester":{"type":"string"}}},"topchain.subscription.MsgCancelDealResponse":{"type":"object"},"topchain.subscription.MsgCreateDeal":{"type":"object","properties":{"amount":{"type":"string","format":"uint64"},"cro_id":{"type":"string"},"end_block":{"type":"string","format":"uint64"},"requester":{"type":"string"},"start_block":{"type":"string","format":"uint64"}}},"topchain.subscription.MsgCreateDealResponse":{"type":"object","properties":{"deal_id":{"type":"string"}}},"topchain.subscription.MsgJoinDeal":{"type":"object","properties":{"deal_id":{"type":"string"},"provider":{"type":"string"}}},"topchain.subscription.MsgJoinDealResponse":{"type":"object"},"topchain.subscription.MsgLeaveDeal":{"type":"object","properties":{"deal_id":{"type":"string"},"provider":{"type":"string"}}},"topchain.subscription.MsgLeaveDealResponse":{"type":"object"},"topchain.subscription.MsgUpdateDeal":{"type":"object","properties":{"amount":{"type":"string","format":"uint64","title":"these fields should be optional, errors with pulsar"},"deal_id":{"type":"string"},"end_block":{"type":"string","format":"uint64"},"requester":{"type":"string"},"start_block":{"type":"string","format":"uint64"}}},"topchain.subscription.MsgUpdateDealResponse":{"type":"object"},"topchain.subscription.MsgUpdateParams":{"description":"MsgUpdateParams is the Msg/UpdateParams request type.","type":"object","properties":{"authority":{"description":"authority is the address that controls the module (defaults to x/gov unless overwritten).","type":"string"},"params":{"description":"NOTE: All parameters must be supplied.","$ref":"#/definitions/topchain.subscription.Params"}}},"topchain.subscription.MsgUpdateParamsResponse":{"description":"MsgUpdateParamsResponse defines the response structure for executing a\nMsgUpdateParams message.","type":"object"},"topchain.subscription.Params":{"description":"Params defines the parameters for the module.","type":"object"},"topchain.subscription.QueryDealResponse":{"type":"object","properties":{"deal":{"$ref":"#/definitions/topchain.subscription.Deal"}}},"topchain.subscription.QueryDealStatusResponse":{"type":"object","properties":{"status":{"$ref":"#/definitions/topchain.subscription.Deal.Status"}}},"topchain.subscription.QueryDealsResponse":{"type":"object","properties":{"deals":{"type":"array","items":{"type":"object","$ref":"#/definitions/topchain.subscription.Deal"}},"pagination":{"$ref":"#/definitions/cosmos.base.query.v1beta1.PageResponse"}}},"topchain.subscription.QueryParamsResponse":{"description":"QueryParamsResponse is response type for the Query/Params RPC method.","type":"object","properties":{"params":{"description":"params holds all the parameters of this module.","$ref":"#/definitions/topchain.subscription.Params"}}},"topchain.subscription.QuerySubscriptionResponse":{"type":"object","properties":{"subscription":{"$ref":"#/definitions/topchain.subscription.Subscription"}}},"topchain.subscription.QuerySubscriptionsResponse":{"type":"object","properties":{"pagination":{"$ref":"#/definitions/cosmos.base.query.v1beta1.PageResponse"},"subscriptions":{"type":"array","items":{"type":"object","$ref":"#/definitions/topchain.subscription.Subscription"}}}},"topchain.subscription.Subscription":{"type":"object","properties":{"deal_id":{"type":"string"},"end_block":{"type":"string","format":"uint64"},"id":{"type":"string"},"provider":{"type":"string"},"start_block":{"type":"string","format":"uint64"}}}},"tags":[{"name":"Query"},{"name":"Msg"}]} \ No newline at end of file +{"id":"topchain","consumes":["application/json"],"produces":["application/json"],"swagger":"2.0","info":{"description":"Chain topchain REST API","title":"HTTP API Console","contact":{"name":"topchain"},"version":"version not set"},"paths":{"/topchain.subscription.Msg/CancelDeal":{"post":{"tags":["Msg"],"operationId":"TopchainMsg_CancelDeal","parameters":[{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/topchain.subscription.MsgCancelDeal"}}],"responses":{"200":{"description":"A successful response.","schema":{"$ref":"#/definitions/topchain.subscription.MsgCancelDealResponse"}},"default":{"description":"An unexpected error response.","schema":{"$ref":"#/definitions/google.rpc.Status"}}}}},"/topchain.subscription.Msg/CreateDeal":{"post":{"tags":["Msg"],"operationId":"TopchainMsg_CreateDeal","parameters":[{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/topchain.subscription.MsgCreateDeal"}}],"responses":{"200":{"description":"A successful response.","schema":{"$ref":"#/definitions/topchain.subscription.MsgCreateDealResponse"}},"default":{"description":"An unexpected error response.","schema":{"$ref":"#/definitions/google.rpc.Status"}}}}},"/topchain.subscription.Msg/JoinDeal":{"post":{"tags":["Msg"],"operationId":"TopchainMsg_JoinDeal","parameters":[{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/topchain.subscription.MsgJoinDeal"}}],"responses":{"200":{"description":"A successful response.","schema":{"$ref":"#/definitions/topchain.subscription.MsgJoinDealResponse"}},"default":{"description":"An unexpected error response.","schema":{"$ref":"#/definitions/google.rpc.Status"}}}}},"/topchain.subscription.Msg/LeaveDeal":{"post":{"tags":["Msg"],"operationId":"TopchainMsg_LeaveDeal","parameters":[{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/topchain.subscription.MsgLeaveDeal"}}],"responses":{"200":{"description":"A successful response.","schema":{"$ref":"#/definitions/topchain.subscription.MsgLeaveDealResponse"}},"default":{"description":"An unexpected error response.","schema":{"$ref":"#/definitions/google.rpc.Status"}}}}},"/topchain.subscription.Msg/UpdateDeal":{"post":{"tags":["Msg"],"operationId":"TopchainMsg_UpdateDeal","parameters":[{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/topchain.subscription.MsgUpdateDeal"}}],"responses":{"200":{"description":"A successful response.","schema":{"$ref":"#/definitions/topchain.subscription.MsgUpdateDealResponse"}},"default":{"description":"An unexpected error response.","schema":{"$ref":"#/definitions/google.rpc.Status"}}}}},"/topchain.subscription.Msg/UpdateParams":{"post":{"tags":["Msg"],"summary":"UpdateParams defines a (governance) operation for updating the module\nparameters. The authority defaults to the x/gov module account.","operationId":"TopchainMsg_UpdateParams","parameters":[{"description":"MsgUpdateParams is the Msg/UpdateParams request type.","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/topchain.subscription.MsgUpdateParams"}}],"responses":{"200":{"description":"A successful response.","schema":{"$ref":"#/definitions/topchain.subscription.MsgUpdateParamsResponse"}},"default":{"description":"An unexpected error response.","schema":{"$ref":"#/definitions/google.rpc.Status"}}}}}},"definitions":{"google.protobuf.Any":{"type":"object","properties":{"@type":{"type":"string"}},"additionalProperties":{}},"google.rpc.Status":{"type":"object","properties":{"code":{"type":"integer","format":"int32"},"details":{"type":"array","items":{"type":"object","$ref":"#/definitions/google.protobuf.Any"}},"message":{"type":"string"}}},"topchain.subscription.MsgCancelDeal":{"type":"object","properties":{"deal_id":{"type":"string"},"requester":{"type":"string"}}},"topchain.subscription.MsgCancelDealResponse":{"type":"object"},"topchain.subscription.MsgCreateDeal":{"type":"object","properties":{"amount":{"type":"string","format":"uint64"},"cro_id":{"type":"string"},"end_block":{"type":"string","format":"uint64"},"requester":{"type":"string"},"start_block":{"type":"string","format":"uint64"}}},"topchain.subscription.MsgCreateDealResponse":{"type":"object","properties":{"deal_id":{"type":"string"}}},"topchain.subscription.MsgJoinDeal":{"type":"object","properties":{"deal_id":{"type":"string"},"provider":{"type":"string"}}},"topchain.subscription.MsgJoinDealResponse":{"type":"object"},"topchain.subscription.MsgLeaveDeal":{"type":"object","properties":{"deal_id":{"type":"string"},"provider":{"type":"string"}}},"topchain.subscription.MsgLeaveDealResponse":{"type":"object"},"topchain.subscription.MsgUpdateDeal":{"type":"object","properties":{"amount":{"type":"string","format":"uint64","title":"these fields should be optional, errors with pulsar"},"deal_id":{"type":"string"},"end_block":{"type":"string","format":"uint64"},"requester":{"type":"string"},"start_block":{"type":"string","format":"uint64"}}},"topchain.subscription.MsgUpdateDealResponse":{"type":"object"},"topchain.subscription.MsgUpdateParams":{"description":"MsgUpdateParams is the Msg/UpdateParams request type.","type":"object","properties":{"authority":{"description":"authority is the address that controls the module (defaults to x/gov unless overwritten).","type":"string"},"params":{"description":"NOTE: All parameters must be supplied.","$ref":"#/definitions/topchain.subscription.Params"}}},"topchain.subscription.MsgUpdateParamsResponse":{"description":"MsgUpdateParamsResponse defines the response structure for executing a\nMsgUpdateParams message.","type":"object"},"topchain.subscription.Params":{"description":"Params defines the parameters for the module.","type":"object"}},"tags":[{"name":"Msg"}]} \ No newline at end of file diff --git a/x/subscription/keeper/deal.go b/x/subscription/keeper/deal.go index f332e0a..6512f97 100644 --- a/x/subscription/keeper/deal.go +++ b/x/subscription/keeper/deal.go @@ -29,20 +29,54 @@ func (k Keeper) GetDeal(ctx sdk.Context, dealId string) (deal types.Deal, found return deal, true } +// check if at least one subscription is active func (k Keeper) IsDealActive(ctx sdk.Context, deal types.Deal) bool { for _, subscriptionId := range deal.SubscriptionIds { subscription, found := k.GetSubscription(ctx, subscriptionId) if !found { continue } - if subscription.EndBlock > uint64(ctx.BlockHeight()) { + if subscription.StartBlock <= uint64(ctx.BlockHeight()) && subscription.EndBlock >= uint64(ctx.BlockHeight()) { return true } } return false } +func (k Keeper) GetAllActiveProviders(ctx sdk.Context, deal types.Deal) []string { + providers := []string{} + for _, subscriptionId := range deal.SubscriptionIds { + subscription, found := k.GetSubscription(ctx, subscriptionId) + if !found { + continue + } + if subscription.StartBlock <= uint64(ctx.BlockHeight()) && subscription.EndBlock >= uint64(ctx.BlockHeight()) { + providers = append(providers, subscription.Provider) + } + } + return providers +} + // Need a formula func (k Keeper) CalculateMinimumStake(ctx sdk.Context, deal types.Deal) int64 { return 0 } + +func (k Keeper) CalculateBlockReward(ctx sdk.Context, deal types.Deal) int64 { + remainingBlocks := deal.EndBlock - uint64(ctx.BlockHeight()) + return int64(deal.AvailableAmount) / int64(remainingBlocks) +} + +// Iterate over all deals and apply the given callback function +func (k Keeper) IterateDeals(ctx sdk.Context, shouldBreak func(deal types.Deal) bool) { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + iterator := prefix.NewStore(storeAdapter, types.KeyPrefix(types.DealKeyPrefix)).Iterator(nil, nil) + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + var deal types.Deal + k.cdc.MustUnmarshal(iterator.Value(), &deal) + if shouldBreak(deal) { + break + } + } +} diff --git a/x/subscription/keeper/msg_deal.go b/x/subscription/keeper/msg_deal.go index baad876..7303c8d 100644 --- a/x/subscription/keeper/msg_deal.go +++ b/x/subscription/keeper/msg_deal.go @@ -44,7 +44,7 @@ func (k msgServer) CancelDeal(goCtx context.Context, msg *types.MsgCancelDeal) ( ctx := sdk.UnwrapSDKContext(goCtx) deal, found := k.GetDeal(ctx, msg.DealId) if !found { - return nil, errorsmod.Wrap(sdkerrors.ErrKeyNotFound, "deal with id " + msg.DealId + " not found") + return nil, errorsmod.Wrap(sdkerrors.ErrKeyNotFound, "deal with id "+msg.DealId+" not found") } if msg.Requester != deal.Requester { return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "only the requester can cancel the deal") @@ -62,7 +62,7 @@ func (k msgServer) CancelDeal(goCtx context.Context, msg *types.MsgCancelDeal) ( for _, subscriptionId := range deal.SubscriptionIds { subscription, found := k.GetSubscription(ctx, subscriptionId) if !found { - return nil, errorsmod.Wrap(sdkerrors.ErrKeyNotFound, "SHOULD NOT HAPPEN: subscription with id " + subscriptionId + " not found") + return nil, errorsmod.Wrap(sdkerrors.ErrKeyNotFound, "SHOULD NOT HAPPEN: subscription with id "+subscriptionId+" not found") } subscription.EndBlock = uint64(ctx.BlockHeight()) k.SetSubscription(ctx, subscription) @@ -78,7 +78,7 @@ func (k msgServer) UpdateDeal(goCtx context.Context, msg *types.MsgUpdateDeal) ( ctx := sdk.UnwrapSDKContext(goCtx) deal, found := k.GetDeal(ctx, msg.DealId) if !found { - return nil, errorsmod.Wrap(sdkerrors.ErrKeyNotFound, "deal with id"+msg.DealId+"not found") + return nil, errorsmod.Wrap(sdkerrors.ErrKeyNotFound, "deal with id "+msg.DealId+" not found") } if msg.Requester != deal.Requester { return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "only the requester can update the deal") @@ -127,7 +127,7 @@ func (k msgServer) JoinDeal(goCtx context.Context, msg *types.MsgJoinDeal) (*typ ctx := sdk.UnwrapSDKContext(goCtx) deal, found := k.GetDeal(ctx, msg.DealId) if !found { - return nil, errorsmod.Wrap(sdkerrors.ErrKeyNotFound, "deal with id"+msg.DealId+"not found") + return nil, errorsmod.Wrap(sdkerrors.ErrKeyNotFound, "deal with id "+msg.DealId+" not found") } delegations := k.stakingKeeper.GetAllDelegations(ctx, sdk.AccAddress(msg.Provider)) @@ -172,12 +172,12 @@ func (k msgServer) LeaveDeal(goCtx context.Context, msg *types.MsgLeaveDeal) (*t ctx := sdk.UnwrapSDKContext(goCtx) deal, found := k.GetDeal(ctx, msg.DealId) if !found { - return nil, errorsmod.Wrap(sdkerrors.ErrKeyNotFound, "deal with id " + msg.DealId + " not found") + return nil, errorsmod.Wrap(sdkerrors.ErrKeyNotFound, "deal with id "+msg.DealId+" not found") } for _, subscriptionId := range deal.SubscriptionIds { subscription, found := k.GetSubscription(ctx, subscriptionId) if !found { - return nil, errorsmod.Wrap(sdkerrors.ErrKeyNotFound, "SHOULD NOT HAPPEN: subscription with id " + subscriptionId + " not found") + return nil, errorsmod.Wrap(sdkerrors.ErrKeyNotFound, "SHOULD NOT HAPPEN: subscription with id "+subscriptionId+" not found") } if subscription.Provider == msg.Provider { subscription.EndBlock = uint64(ctx.BlockHeight()) diff --git a/x/subscription/module/module.go b/x/subscription/module/module.go index 5009f9c..2e8206e 100644 --- a/x/subscription/module/module.go +++ b/x/subscription/module/module.go @@ -151,10 +151,64 @@ func (am AppModule) BeginBlock(_ context.Context) error { // EndBlock contains the logic that is automatically triggered at the end of each block. // The end block implementation is optional. -func (am AppModule) EndBlock(_ context.Context) error { +func (am AppModule) EndBlock(goCtx context.Context) error { + ctx := sdk.UnwrapSDKContext(goCtx) + am.keeper.IterateDeals(ctx, func(deal types.Deal) bool { + // deal status updates + // return false to callback to continue iteration + switch deal.Status { + case types.Deal_EXPIRED: + return false + case types.Deal_CANCELLED: + return false + case types.Deal_SCHEDULED: + if uint64(ctx.BlockHeight()) >= deal.StartBlock { + deal.Status = types.Deal_ACTIVE + deal = am.PayActiveProvidersPerBlock(ctx, deal) + } else { + deal.Status = types.Deal_INITIALIZED + } + case types.Deal_INITIALIZED: + if uint64(ctx.BlockHeight()) > deal.EndBlock { + deal.Status = types.Deal_EXPIRED + // return the remaining amount to the requester + am.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, sdk.AccAddress(deal.Requester), sdk.NewCoins(sdk.NewInt64Coin("top", int64(deal.AvailableAmount)))) + } + case types.Deal_ACTIVE: + if uint64(ctx.BlockHeight()) > deal.EndBlock { + deal.Status = types.Deal_EXPIRED + // return the remaining amount to the requester + am.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, sdk.AccAddress(deal.Requester), sdk.NewCoins(sdk.NewInt64Coin("top", int64(deal.AvailableAmount)))) + } else { + deal = am.PayActiveProvidersPerBlock(ctx, deal) + } + case types.Deal_INACTIVE: + if uint64(ctx.BlockHeight()) > deal.EndBlock { + deal.Status = types.Deal_EXPIRED + // return the remaining amount to the requester + am.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, sdk.AccAddress(deal.Requester), sdk.NewCoins(sdk.NewInt64Coin("top", int64(deal.AvailableAmount)))) + } + default: + return false + } + + am.keeper.SetDeal(ctx, deal) + return false + }) return nil } +func (am AppModule) PayActiveProvidersPerBlock(ctx sdk.Context, deal types.Deal) types.Deal { + activeProviders := am.keeper.GetAllActiveProviders(ctx, deal) + blockReward := am.keeper.CalculateBlockReward(ctx, deal) + rewardPerProvider := blockReward / int64(len(activeProviders)) + for _, provider := range activeProviders { + am.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, sdk.AccAddress(provider), sdk.NewCoins(sdk.NewInt64Coin("top", rewardPerProvider))) + } + deal.AvailableAmount -= uint64(blockReward) + return deal +} + // IsOnePerModuleType implements the depinject.OnePerModuleType interface. func (am AppModule) IsOnePerModuleType() {}