Skip to content

feat(x/provider): withdraw funds from consumer pool address at each block#10

Open
Pantani wants to merge 22 commits intomainfrom
Pantani/feat/incentive-payment
Open

feat(x/provider): withdraw funds from consumer pool address at each block#10
Pantani wants to merge 22 commits intomainfrom
Pantani/feat/incentive-payment

Conversation

@Pantani
Copy link
Contributor

@Pantani Pantani commented Jan 28, 2026

close #9

@Pantani Pantani self-assigned this Jan 28, 2026
@Pantani Pantani marked this pull request as ready for review February 3, 2026 21:34
@Pantani Pantani marked this pull request as draft February 5, 2026 16:56
@Pantani Pantani marked this pull request as ready for review February 5, 2026 21:05
@Pantani Pantani requested a review from clockworkgr as a code owner February 5, 2026 21:05
Copy link
Contributor

@tbruyelle tbruyelle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some small comments but overall LGTM, thanks !

…e-payment

# Conflicts:
#	x/vaas/provider/types/provider.pb.go
@Pantani Pantani removed the request for review from giuliostramondo February 18, 2026 15:29
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements per-block fee withdrawal from consumer funding accounts and distribution to provider validators, adding a new fees_per_block parameter to configure the charge (Issue #9).

Changes:

  • Add fees_per_block to provider params (proto + Go types + validation) and update related tests.
  • Add per-block fee collection from consumers and proportional distribution to bonded validators.
  • Extend expected keeper interfaces + mocks for new staking/bank methods used by fee logic.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
x/vaas/types/expected_keepers.go Extends staking/bank keeper interfaces for total power, address codec, and module→account sends.
proto/vaas/provider/v1/provider.proto Adds fees_per_block param to proto schema.
x/vaas/provider/types/provider.pb.go Regenerated protobuf output reflecting new params field.
x/vaas/provider/types/params.go Adds default fees, wires param through constructors/defaults, and validates FeesPerBlock.
x/vaas/provider/types/params_test.go Updates params validation tests to include fees and adds a zero-fee invalid case.
x/vaas/provider/types/genesis_test.go Updates genesis tests to pass the new param.
x/vaas/provider/module.go Calls fee collection + distribution in BeginBlock.
x/vaas/provider/keeper/params.go Exposes GetFeesPerBlock from keeper params.
x/vaas/provider/keeper/params_test.go Updates keeper params test to include fees.
x/vaas/provider/keeper/fees.go New keeper logic to collect fees from consumers and distribute to validators.
x/vaas/provider/keeper/fees_test.go New unit tests for fee collection and distribution.
testutil/keeper/mocks.go Regenerates mocks for newly added expected keeper methods.
.gitignore Formatting-only change.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +51 to +56
consumerModuleName := fmt.Sprintf("consumer-%s", consumerId)
feeCoins := sdk.NewCoins(feesPerBlock)
if err := k.bankKeeper.SendCoinsFromModuleToModule(ctx, consumerModuleName, k.feeCollectorName, feeCoins); err != nil {
return totalFeesCollected, fmt.Errorf("failed to collect fees from consumer (%s): %w", consumerId, err)
}

Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code attempts to send coins from a module account named "consumer-" (e.g., "consumer-0", "consumer-1"), but there is no visible code in this PR that creates these module accounts or registers them with the auth keeper's module account permissions. Module accounts must be explicitly registered in the maccPerms map during app initialization. Without proper registration, SendCoinsFromModuleToModule will fail at runtime. You should either add these module accounts to the maccPerms map in app/provider/app.go, or use a different approach that doesn't require dynamically named module accounts.

Suggested change
consumerModuleName := fmt.Sprintf("consumer-%s", consumerId)
feeCoins := sdk.NewCoins(feesPerBlock)
if err := k.bankKeeper.SendCoinsFromModuleToModule(ctx, consumerModuleName, k.feeCollectorName, feeCoins); err != nil {
return totalFeesCollected, fmt.Errorf("failed to collect fees from consumer (%s): %w", consumerId, err)
}
feeCoins := sdk.NewCoins(feesPerBlock)
if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, consumerModuleAccAddr, k.feeCollectorName, feeCoins); err != nil {
return totalFeesCollected, fmt.Errorf("failed to collect fees from consumer (%s): %w", consumerId, err)
}

Copilot uses AI. Check for mistakes.
Comment on lines +116 to +120
// Send coins from fee_collector to validator account
coins := sdk.NewCoins(sdk.NewCoin(totalFees.Denom, valShare))
if err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, k.feeCollectorName, valAddr, coins); err != nil {
return fmt.Errorf("failed to send fees to validator %s: %w", val.GetOperator(), err)
}
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fee distribution logic sends coins directly from the fee collector to validator operator addresses. This bypasses the standard staking module's delegation distribution mechanism, which means that delegators will not receive their share of these fees. In typical Cosmos SDK chains, fees collected in the fee_collector module account are distributed through the distribution module (x/distribution), which ensures that both validators and their delegators receive rewards proportionally. Consider using the distribution module's AllocateTokensToValidator or a similar mechanism to ensure proper reward distribution to delegators.

Suggested change
// Send coins from fee_collector to validator account
coins := sdk.NewCoins(sdk.NewCoin(totalFees.Denom, valShare))
if err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, k.feeCollectorName, valAddr, coins); err != nil {
return fmt.Errorf("failed to send fees to validator %s: %w", val.GetOperator(), err)
}
// Allocate fees to validator via distribution module so delegators also receive rewards.
decCoins := sdk.NewDecCoinsFromCoins(sdk.NewCoin(totalFees.Denom, valShare))
k.distributionKeeper.AllocateTokensToValidator(ctx, val, decCoins)

Copilot uses AI. Check for mistakes.
Comment on lines +102 to +110
// Calculate validator's share: (validatorPower / totalPower) * totalFees
powerReduction := k.stakingKeeper.PowerReduction(ctx)
valPower := math.NewInt(val.GetConsensusPower(powerReduction))
valShare := totalFees.Amount.Mul(valPower).Quo(totalPower)

// If this is the last validator, assign the remainder to ensure all fees are distributed
if i == len(validators)-1 {
valShare = totalFees.Amount.Sub(distributedAmount)
}
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When totalFees is zero or very small and there are many validators, the integer division in line 105 may result in all validators receiving zero tokens due to rounding, even though totalFees is non-zero. The remainder-to-last-validator logic on lines 108-110 only helps if at least one validator gets a non-zero share. Consider adding a check to skip distribution if totalFees.IsZero() or if totalFees is too small to meaningfully distribute.

Copilot uses AI. Check for mistakes.
Comment on lines +161 to +165
sdkCtx.Logger().Error("failed to collect fees from consumers", "err", err)
} else {
// Distribute collected fees to validators. Failures here should also not halt block processing.
if err := am.keeper.DistributeFeesToValidators(sdkCtx, collectedFees); err != nil {
sdkCtx.Logger().Error("failed to distribute collected fees to validators", "err", err)
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error messages logged when fee collection or distribution fails provide limited context about the state of the system. Consider adding more diagnostic information such as the number of consumers processed, the number of successful collections, and the total amount of fees that failed to be collected or distributed. This would aid in debugging and operational monitoring.

Suggested change
sdkCtx.Logger().Error("failed to collect fees from consumers", "err", err)
} else {
// Distribute collected fees to validators. Failures here should also not halt block processing.
if err := am.keeper.DistributeFeesToValidators(sdkCtx, collectedFees); err != nil {
sdkCtx.Logger().Error("failed to distribute collected fees to validators", "err", err)
sdkCtx.Logger().Error(
"failed to collect fees from consumers",
"err", err,
"height", sdkCtx.BlockHeight(),
)
} else {
// Distribute collected fees to validators. Failures here should also not halt block processing.
if err := am.keeper.DistributeFeesToValidators(sdkCtx, collectedFees); err != nil {
sdkCtx.Logger().Error(
"failed to distribute collected fees to validators",
"err", err,
"height", sdkCtx.BlockHeight(),
"collected_fees", collectedFees.String(),
)

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

x/provider: withdraw funds from consumer pool address at each block

3 participants