ExocoreNetwork / exocore

Omnichain Restaking
7 stars 9 forks source link

feat(distribution): add fee-distribution and avs-reward distribution #117

Closed mikebraver closed 1 week ago

mikebraver commented 3 months ago

Description

Add the distribution module logic and minting reward related. The distribution mainly contains two parts:

  1. The fee distribution to exocore validator and corresponding stakers, which come from the minting rewards.
  2. The reward allocation/distribution to avs, which is based on the commission of avs params as well as reward calculation for avs. And generally, these will be allocated on epoch-basis, say, one day. And upon the epoch end, all these two hooks will be triggered to distribute the reward.

Summary by CodeRabbit

Summary by CodeRabbit

coderabbitai[bot] commented 3 months ago

[!IMPORTANT]

Review skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

The update integrates a new fee distribution module into the Exocore application, enhancing its reward and incentive mechanisms. Key modifications include the introduction of a token allocation system for distributing fees among validators and stakers, adjustments to commission rates for operators, and improvements to the testing framework to support these changes. Additionally, various interfaces and keeper functions have been updated to facilitate better interactions within the system.

Changes

File/Path Change Summary
app/app.go Integrated fee distribution module with imports, module registration, keeper initialization, and epoch hooks.
local_node.sh Updated commission rates for operators from "0.0" to "1.0" for multiple parameters.
testutil/utils.go Added functionality to set up genesis state for the distribution module in the test suite.
x/dogfood/keeper/keeper.go Modified NewKeeper function signature and added methods for enhanced delegation management.
x/dogfood/types/expected_keepers.go Introduced new methods in OperatorKeeper, DelegationKeeper, and AVSKeeper interfaces for improved data management.
x/feedistribution/keeper/allocation.go Implemented token allocation mechanism for distributing fees among validators and stakers.
x/feedistribution/keeper/hooks_test.go Created tests for epoch hooks to validate behavior during epoch transitions.
x/operator/keeper/usd_value_test.go Updated test constants to reference the assetstype package for clarity and maintainability.

Possibly related PRs

Suggested labels

Type: Build

Suggested reviewers

Poem

In the code where changes bloom,
Dependencies rise and groom,
Tokens dance in keeper's light,
Validators cheer, stakers delight.
A rabbit hops with joy so clear,
For the future, we hold dear! 🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share - [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai) - [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai) - [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai) - [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)
Tips ### Chat There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai): - Review comments: Directly reply to a review comment made by CodeRabbit. Example: -- `I pushed a fix in commit , please review it.` -- `Generate unit testing code for this file.` - `Open a follow-up GitHub issue for this discussion.` - Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples: -- `@coderabbitai generate unit testing code for this file.` -- `@coderabbitai modularize this function.` - PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples: -- `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.` -- `@coderabbitai read src/utils.ts and generate unit testing code.` -- `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.` -- `@coderabbitai help me debug CodeRabbit configuration file.` Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. ### CodeRabbit Commands (Invoked using PR comments) - `@coderabbitai pause` to pause the reviews on a PR. - `@coderabbitai resume` to resume the paused reviews. - `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository. - `@coderabbitai full review` to do a full review from scratch and review all the files again. - `@coderabbitai summary` to regenerate the summary of the PR. - `@coderabbitai resolve` resolve all the CodeRabbit review comments. - `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository. - `@coderabbitai help` to get help. ### Other keywords and placeholders - Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed. - Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description. - Add `@coderabbitai` anywhere in the PR title to generate the title automatically. ### CodeRabbit Configuration File (`.coderabbit.yaml`) - You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository. - Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information. - If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json` ### Documentation and Community - Visit our [Documentation](https://coderabbit.ai/docs) for detailed information on how to use CodeRabbit. - Join our [Discord Community](https://discord.com/invite/GsXnASn26c) to get help, request features, and share feedback. - Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.
cloud8little commented 1 month ago

Test failed, running local_node.sh to start a single validator network, check distribution logs, there is no logs for single staker, it is due to the operatorAddress used is not Bench32 address. The code use operatorAddress.String() , use log to print it out, the result is exovaloper18cggcpvwspnd5c6ny8wrqxpffj5zmhkl0le8yd, it should be exo18cggcpvwspnd5c6ny8wrqxpffj5zmhklprtnph

10:06AM INF Allocate tokens to all validators feesCollected amount is =20.000000000000000000aexo module=x/distribution
10:06AM INF avsList is=[] module=x/distribution
10:06AM INF operatorAddress is=exovaloper18cggcpvwspnd5c6ny8wrqxpffj5zmhkl0le8yd module=x/distribution
10:06AM INF staker map is={} module=x/distribution
10:06AM INF allocate tokens to stakers successfully allocated amount is=19.400000000000000000aexo module=x/distribution
10:06AM INF Allocate tokens to validator successfully allocated amount is=19.400000000000000000aexo module=x/distribution
func (k Keeper) AllocateTokensToStakers(ctx sdk.Context, operatorAddress sdk.ValAddress, rewardToAllStakers sdk.DecCoins, feePool *types.FeePool) {
    logger := k.Logger()
    avsList, err := k.StakingKeeper.GetOptedInAVSForOperator(ctx, operatorAddress.String())
    if err != nil {
        ctx.Logger().Error("avs address lists not found; skipping")
    }
    stakersPowerMap, curTotoalStakersPowers := make(map[string]math.LegacyDec), math.LegacyNewDec(1)
    for _, avsAddress := range avsList {
        avsAssets, err := k.StakingKeeper.GetAVSSupportedAssets(ctx, avsAddress)
        if err != nil {
            ctx.Logger().Error("avs address lists not found; skipping")
        }
        for assetID := range avsAssets {
            stakerList, err := k.StakingKeeper.GetStakersByOperator(ctx, operatorAddress.String(), assetID)
            if err != nil {
                ctx.Logger().Error("staker lists not found; skipping")
            }
            for _, staker := range stakerList.Stakers {
                if curStakerPower, err := k.StakingKeeper.CalculateUSDValueForStaker(ctx, staker, avsAddress, operatorAddress.Bytes()); err != nil {
                    ctx.Logger().Error("curStakerPower error", err)
                } else {
                    stakersPowerMap[staker] = curStakerPower
                    curTotoalStakersPowers.Add(curStakerPower)
                }
            }
        }
    }

    for staker, stakerPower := range stakersPowerMap {
        powerFraction := stakerPower.QuoTruncate(curTotoalStakersPowers)
        rewardToSingleStaker := rewardToAllStakers.MulDecTruncate(powerFraction)
        k.AllocateTokensToSingleStaker(ctx, staker, rewardToSingleStaker)
        rewardToAllStakers = rewardToAllStakers.Sub(rewardToSingleStaker)
    }
    feePool.CommunityPool = feePool.CommunityPool.Add(rewardToAllStakers...)
    logger.Info("allocate tokens to stakers successfully", "allocated amount is", rewardToAllStakers.String())
}
leonz789 commented 1 month ago

For the F1 distribution. existing implementation actually support for 1->n assets diestribution(single staking assets, multiple reward assets). For our multi-assets staking platform, we can wrap all different kinds of assets into one single assets-USDT, then it will just suit into F1 solution

mikebraver commented 1 month ago

For the F1 distribution. existing implementation actually support for 1->n assets diestribution(single staking assets, multiple reward assets). For our multi-assets staking platform, we can wrap all different kinds of assets into one single assets-USDT, then it will just suit into F1 solution

F1 is used for optimization, and for the short-term that the stakers-assets amount can be acceptable to calculate by our current implement, we can further optimize it in next phase

cloud8little commented 1 month ago

Test failed on multiple assets, initialize five validators, two assets for the chain, can notice there is no log for single staker, which means staker don't get reward distributed. Notably, there is an outstanding log 10:10AM ERR staker lists not found; skipping module=server that breaks the distribution process of each staker.

for assetID := range avsAssets {
    stakerList, err := k.StakingKeeper.GetStakersByOperator(ctx, operatorAddress.String(), assetID)
    if err != nil {
        ctx.Logger().Error("staker lists not found; skipping")
    }
    for _, staker := range stakerList.Stakers {
        if curStakerPower, err := k.StakingKeeper.CalculateUSDValueForStaker(ctx, staker, avsAddress, operatorAddress.Bytes()); err != nil {
            ctx.Logger().Error("curStakerPower error", err)
        } else {
            stakersPowerMap[staker] = curStakerPower
            logger.Info("curStakerPower", "is", curStakerPower.String())
            globalStakerAddressList = append(globalStakerAddressList, staker)
            curTotalStakersPowers = curTotalStakersPowers.Add(curStakerPower)
            logger.Info("curTotalStakersPowers", "is", curTotalStakersPowers.String())
        }
    }
}
 2024/08/28 10:10:33 Triggered, height:6446760, feeder-parames:{feederID:2, startBlock:6456694, interval:30, roundID:1}:
 2024/08/28 10:10:33 Triggered, height:6446760, feeder-parames:{feederID:1, startBlock:6456694, interval:30, roundID:1}:
 10:10AM INF Timed out dur=2987.346754 height=6446761 module=consensus round=0 server=node step=RoundStepNewHeight
 10:10AM INF received proposal module=consensus proposal="Proposal{6446761/0 (7F5F6825DB287D3970F9197642E020A381DB57B11067FC73F1995DD85A837F4D:1:167C6D51AD55, -1) 4522411604A7 @ 2024-08-28T10:10:36.434796761Z}" proposer=9D9F577A10F6C83B928C0268CA3C261CA4412FAE server=node
 10:10AM INF received complete proposal block hash=7F5F6825DB287D3970F9197642E020A381DB57B11067FC73F1995DD85A837F4D height=6446761 module=consensus server=node
 10:10AM INF finalizing commit of block hash=7F5F6825DB287D3970F9197642E020A381DB57B11067FC73F1995DD85A837F4D height=6446761 module=consensus num_txs=0 root=9B0718A6D05C85CB47D29DE4B6452E70DC1FA60E9109032434B73E14246F0609 server=node
 10:10AM INF ending epoch identifier=minute module=x/epochs number=67
 10:10AM INF minted coins from module account amount=20000000000000000000aexo from=exomint module=x/bank
 10:10AM INF AfterEpochEnd minted successfully=20000000000000000000aexo module=x/exomint
 10:10AM INF AfterEpochEnd of distribution module=x/distribution
 10:10AM INF feeCollectorName is=fee_collector module=x/distribution
 10:10AM INF exomint module account is=exo17xpfvakm2amg962yls6f84z3kell8c5lgqz40d module=x/distribution
 10:10AM INF distribution module name is=distribution module=x/distribution
 10:10AM INF distribution module account is=exo17xpfvakm2amg962yls6f84z3kell8c5lgqz40d module=x/distribution
 10:10AM INF Allocate tokens to all validators feesCollected amount is =20000000000000000000.000000000000000000aexo module=x/distribution
 10:10AM INF Allocate to stakers of operatorAddress: module=x/distribution
 10:10AM INF operator address is=exo1uw7n7xqxj2hmk5dlw92as6s7se5mmkafuwx982 module=x/distribution
 10:10AM INF assetID is=0x83e6850591425e3c1e263c054f4466838b9bd9e4_0x9ce1 module=x/distribution
 10:10AM INF operator address is=exo1uw7n7xqxj2hmk5dlw92as6s7se5mmkafuwx982 module=x/distribution
 10:10AM INF assetID is=0xb82381a3fbd3fafa77b3a7be693342618240067b_0x9ce1 module=x/distribution
 10:10AM ERR staker lists not found; skipping module=server
 10:10AM INF allocate tokens to stakers successfully allocated amount is=5134998402969381034.000000000000000000aexo module=x/distribution
 10:10AM INF Allocate tokens to validator successfully allocated amount is=344116216810837414575.681431837329290994aexo module=x/distribution
 10:10AM INF Allocate to stakers of operatorAddress: module=x/distribution
 10:10AM INF operator address is=exo1c5x7mxphvgavjhu0au9jjqnfqcyspevtyy27mz module=x/distribution
 10:10AM INF assetID is=0x83e6850591425e3c1e263c054f4466838b9bd9e4_0x9ce1 module=x/distribution
 10:10AM INF operator address is=exo1c5x7mxphvgavjhu0au9jjqnfqcyspevtyy27mz module=x/distribution
 10:10AM INF assetID is=0xb82381a3fbd3fafa77b3a7be693342618240067b_0x9ce1 module=x/distribution
 10:10AM ERR staker lists not found; skipping module=server
 10:10AM INF allocate tokens to stakers successfully allocated amount is=5230090965987332529.600000000000000000aexo module=x/distribution
 10:10AM INF Allocate tokens to validator successfully allocated amount is=350488739344371440434.472338826946440838aexo module=x/distribution
 10:10AM INF Allocate to stakers of operatorAddress: module=x/distribution
 10:10AM INF operator address is=exo1u25udsfm5wu3h5s5xqayf6jjycy2etwyfctk5w module=x/distribution
 10:10AM INF assetID is=0x83e6850591425e3c1e263c054f4466838b9bd9e4_0x9ce1 module=x/distribution
 10:10AM INF operator address is=exo1u25udsfm5wu3h5s5xqayf6jjycy2etwyfctk5w module=x/distribution
 10:10AM INF assetID is=0xb82381a3fbd3fafa77b3a7be693342618240067b_0x9ce1 module=x/distribution
 10:10AM ERR staker lists not found; skipping module=server
 10:10AM INF allocate tokens to stakers successfully allocated amount is=3328239705628302520.600000000000000000aexo module=x/distribution
 10:10AM INF Allocate tokens to validator successfully allocated amount is=223038288673690916758.306893892459444166aexo module=x/distribution
 10:10AM INF Allocate to stakers of operatorAddress: module=x/distribution
 10:10AM INF operator address is=exo1nvgrxlcn854xyrf0jzmrl353gduznvneyassn6 module=x/distribution
 10:10AM INF assetID is=0x83e6850591425e3c1e263c054f4466838b9bd9e4_0x9ce1 module=x/distribution
 10:10AM ERR staker lists not found; skipping module=server
 10:10AM INF operator address is=exo1nvgrxlcn854xyrf0jzmrl353gduznvneyassn6 module=x/distribution
 10:10AM INF assetID is=0xb82381a3fbd3fafa77b3a7be693342618240067b_0x9ce1 module=x/distribution
 10:10AM ERR staker lists not found; skipping module=server
 10:10AM INF allocate tokens to stakers successfully allocated amount is=381487396409699813.000000000000000000aexo module=x/distribution
 10:10AM INF Allocate tokens to validator successfully allocated amount is=25564954321623556697.858245512986033479aexo module=x/distribution
 10:10AM INF Allocate to stakers of operatorAddress: module=x/distribution
 10:10AM INF operator address is=exo1lzf5247us2xwhg29mutpx5gyq4aemwqgc08ev7 module=x/distribution
 10:10AM INF assetID is=0x83e6850591425e3c1e263c054f4466838b9bd9e4_0x9ce1 module=x/distribution
 10:10AM INF operator address is=exo1lzf5247us2xwhg29mutpx5gyq4aemwqgc08ev7 module=x/distribution
 10:10AM INF assetID is=0xb82381a3fbd3fafa77b3a7be693342618240067b_0x9ce1 module=x/distribution
 10:10AM ERR staker lists not found; skipping module=server
 10:10AM INF allocate tokens to stakers successfully allocated amount is=5325183529005284044.600000000000000000aexo module=x/distribution
 10:10AM INF Allocate tokens to validator successfully allocated amount is=356861261877905467593.332706844992390641aexo module=x/distribution
 10:10AM INF executed block height=6446761 module=state num_invalid_txs=0 num_valid_txs=0 server=node
MaxMustermann2 commented 1 week ago

Minor changes suggested; looks good.