diff --git a/x/manifest/keeper/keeper.go b/x/manifest/keeper/keeper.go index 2923bdc..3ea41d7 100644 --- a/x/manifest/keeper/keeper.go +++ b/x/manifest/keeper/keeper.go @@ -11,6 +11,7 @@ import ( sdkmath "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper" ) @@ -80,6 +81,15 @@ func (k *Keeper) ExportGenesis(ctx context.Context) *types.GenesisState { } } +func (k *Keeper) GetShareHolders(ctx context.Context) []*types.StakeHolders { + params, err := k.Params.Get(ctx) + if err != nil { + panic(err) + } + + return params.StakeHolders +} + // IsManualMintingEnabled returns nil if inflation mint is 0% (disabled) func (k Keeper) IsManualMintingEnabled(ctx context.Context) error { minter, err := k.mintKeeper.Minter.Get(ctx) @@ -94,3 +104,25 @@ func (k Keeper) IsManualMintingEnabled(ctx context.Context) error { return ErrManualMintingDisabled.Wrapf("inflation: %s", minter.Inflation.String()) } + +// Returns the amount of coins to be distributed to the holders +func (k Keeper) CalculateShareHolderTokenPayout(ctx context.Context, c sdk.Coin) map[string]sdk.Coin { + sh := k.GetShareHolders(ctx) + + pairs := make(map[string]sdk.Coin, len(sh)) + + // iter each stakeholder, get their percent of the total 100%, and then split up their amount of coin cost + for _, s := range sh { + pct := sdkmath.NewInt(int64(s.Percentage)).ToLegacyDec().QuoInt64(types.MaxPercentShare) + coinAmt := pct.MulInt(c.Amount).RoundInt() + + if coinAmt.IsZero() { + // too small of an amount to matter (< 1 utoken) + continue + } + + pairs[s.Address] = sdk.NewCoin(c.Denom, coinAmt) + } + + return pairs +} diff --git a/x/manifest/keeper/msg_server_test.go b/x/manifest/keeper/msg_server_test.go index 0666cf4..070a7e2 100644 --- a/x/manifest/keeper/msg_server_test.go +++ b/x/manifest/keeper/msg_server_test.go @@ -3,12 +3,66 @@ package keeper_test import ( "testing" + sdkmath "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/testutil/testdata" "github.com/liftedinit/manifest-ledger/x/manifest/keeper" "github.com/liftedinit/manifest-ledger/x/manifest/types" "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" ) +func TestCalculatePayoutLogic(t *testing.T) { + _, _, authority := testdata.KeyTestPubAddr() + _, _, acc := testdata.KeyTestPubAddr() + _, _, acc2 := testdata.KeyTestPubAddr() + _, _, acc3 := testdata.KeyTestPubAddr() + _, _, acc4 := testdata.KeyTestPubAddr() + + f := initFixture(t) + + k := f.App.ManifestKeeper + + k.SetAuthority(f.Ctx, authority.String()) + ms := keeper.NewMsgServerImpl(k) + + sh := []*types.StakeHolders{ + { + Address: acc.String(), + Percentage: 50_000_000, // 50% + }, + { + Address: acc2.String(), + Percentage: 49_000_000, + }, + { + Address: acc3.String(), + Percentage: 500_001, // 0.5% + }, + { + Address: acc4.String(), + Percentage: 499_999, + }, + } + + _, err := ms.UpdateParams(f.Ctx, &types.MsgUpdateParams{ + Authority: authority.String(), + Params: types.NewParams(sh), + }) + require.NoError(t, err) + + // validate the full payout of 100 tokens got split up between all fractional shares as expected + res := k.CalculateShareHolderTokenPayout(f.Ctx, sdk.NewCoin("stake", sdkmath.NewInt(100_000_000))) + for _, s := range sh { + for w, coin := range res { + if s.Address == w { + require.EqualValues(t, s.Percentage, coin.Amount.Int64()) + } + } + } + +} + func TestUpdateParams(t *testing.T) { _, _, authority := testdata.KeyTestPubAddr() _, _, acc := testdata.KeyTestPubAddr() diff --git a/x/manifest/types/params.go b/x/manifest/types/params.go index 0889103..9bd27cd 100644 --- a/x/manifest/types/params.go +++ b/x/manifest/types/params.go @@ -7,7 +7,7 @@ import ( const ( // uses a precision of 6 decimal places. - maxPercentShare = 100_000_000 + MaxPercentShare = 100_000_000 ) // DefaultParams returns default module parameters. @@ -47,8 +47,8 @@ func (p Params) Validate() error { total += int64(sh.Percentage) } - if total != maxPercentShare { - return fmt.Errorf("stakeholders should add up to %d, got %d", maxPercentShare, total) + if total != MaxPercentShare { + return fmt.Errorf("stakeholders should add up to %d, got %d", MaxPercentShare, total) } seen := make(map[string]struct{})