package types_test

import (
	"encoding/json"
	"strconv"
	"testing"

	cosmosMath "cosmossdk.io/math"
	alloraMath "github.com/allora-network/allora-chain/math"
	"github.com/allora-network/allora-chain/x/emissions/types"

	sdk "github.com/cosmos/cosmos-sdk/types"
	"github.com/stretchr/testify/require"
)

const (
	AttributeKeyActorType     = "actor_type"
	AttributeKeyTopicId       = "topic_id"
	AttributeKeyBlockHeight   = "block_height"
	AttributeKeyBlockHeightTx = "block_height_tx"
	AttributeKeyAddresses     = "addresses"
	AttributeKeyScores        = "scores"
	AttributeKeyRewards       = "rewards"
	AttributeKeyValueBundle   = "bundle"
	AttributeKeyCoefficients  = "coefficients"
	AttributeKeyRegrets       = "regrets"
	AttributeKeyRegret        = "regret"
	AttributeKeyWeights       = "weights"
)

func TestEmitNewInfererScoresSetEventWithScores(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	scores := []types.Score{
		{
			TopicId:     uint64(1),
			BlockHeight: int64(10),
			Address:     "address1",
			Score:       alloraMath.NewDecFromInt64(100),
		},
		{
			TopicId:     uint64(1),
			BlockHeight: int64(10),
			Address:     "address2",
			Score:       alloraMath.NewDecFromInt64(200),
		},
	}

	types.EmitNewActorScoresSetEvent(ctx, types.ActorType_ACTOR_TYPE_INFERER_UNSPECIFIED, 10, scores)

	events := ctx.EventManager().Events()
	require.Len(t, events, 1)

	event := events[0]
	require.Equal(t, "emissions.v9.EventScoresSet", event.Type)

	attributes := event.Attributes
	require.Len(t, attributes, 5)

	val, exists := event.GetAttribute(AttributeKeyActorType)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "INFERER")

	val, exists = event.GetAttribute(AttributeKeyTopicId)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "1")

	val, exists = event.GetAttribute(AttributeKeyBlockHeight)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "10")

	val, exists = event.GetAttribute(AttributeKeyAddresses)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), `["address1","address2"]`)

	val, exists = event.GetAttribute(AttributeKeyScores)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), `["100","200"]`)
}

func TestEmitNewInfererScoresSetEventWithNoScores(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	scores := []types.Score{}

	types.EmitNewActorScoresSetEvent(ctx, types.ActorType_ACTOR_TYPE_INFERER_UNSPECIFIED, 10, scores)

	events := ctx.EventManager().Events()
	require.Empty(t, events)
}

func TestEmitNewForecasterScoresSetEventWithScores(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	scores := []types.Score{
		{
			TopicId:     uint64(1),
			BlockHeight: int64(10),
			Address:     "address1",
			Score:       alloraMath.NewDecFromInt64(100),
		},
		{
			TopicId:     uint64(1),
			BlockHeight: int64(10),
			Address:     "address2",
			Score:       alloraMath.NewDecFromInt64(200),
		},
	}

	types.EmitNewActorScoresSetEvent(ctx, types.ActorType_ACTOR_TYPE_FORECASTER, 10, scores)

	events := ctx.EventManager().Events()
	require.Len(t, events, 1)

	event := events[0]
	require.Equal(t, "emissions.v9.EventScoresSet", event.Type)

	attributes := event.Attributes
	require.Len(t, attributes, 5)

	val, exists := event.GetAttribute(AttributeKeyActorType)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "FORECASTER")

	val, exists = event.GetAttribute(AttributeKeyTopicId)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "1")

	val, exists = event.GetAttribute(AttributeKeyBlockHeight)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "10")

	val, exists = event.GetAttribute(AttributeKeyAddresses)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), `["address1","address2"]`)

	val, exists = event.GetAttribute(AttributeKeyScores)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), `["100","200"]`)
}

func TestEmitNewForecasterScoresSetEventWithNoScores(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	scores := []types.Score{}

	types.EmitNewActorScoresSetEvent(ctx, types.ActorType_ACTOR_TYPE_FORECASTER, 10, scores)

	events := ctx.EventManager().Events()
	require.Empty(t, events)
}

func TestEmitNewReputerScoresSetEventWithScores(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	scores := []types.Score{
		{
			TopicId:     uint64(1),
			BlockHeight: int64(10),
			Address:     "address1",
			Score:       alloraMath.NewDecFromInt64(100),
		},
		{
			TopicId:     uint64(1),
			BlockHeight: int64(10),
			Address:     "address2",
			Score:       alloraMath.NewDecFromInt64(200),
		},
	}

	types.EmitNewActorScoresSetEvent(ctx, types.ActorType_ACTOR_TYPE_REPUTER, 10, scores)

	events := ctx.EventManager().Events()
	require.Len(t, events, 1)

	event := events[0]
	require.Equal(t, "emissions.v9.EventScoresSet", event.Type)

	attributes := event.Attributes
	require.Len(t, attributes, 5)

	val, exists := event.GetAttribute(AttributeKeyActorType)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "REPUTER")

	val, exists = event.GetAttribute(AttributeKeyTopicId)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "1")

	val, exists = event.GetAttribute(AttributeKeyBlockHeight)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "10")

	val, exists = event.GetAttribute(AttributeKeyAddresses)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), `["address1","address2"]`)

	val, exists = event.GetAttribute(AttributeKeyScores)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), `["100","200"]`)
}

func TestEmitNewReputerScoresSetEventWithNoScores(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	scores := []types.Score{}

	types.EmitNewActorScoresSetEvent(ctx, types.ActorType_ACTOR_TYPE_REPUTER, 10, scores)

	events := ctx.EventManager().Events()
	require.Empty(t, events)
}

func TestEmitNewInfererRewardsSettledEventWithRewards(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	rewards := []types.TaskReward{
		{
			TopicId: uint64(1),
			Address: "address1",
			Reward:  alloraMath.NewDecFromInt64(100),
			Type:    types.WorkerInferenceRewardType,
		},
		{
			TopicId: uint64(1),
			Address: "address2",
			Reward:  alloraMath.NewDecFromInt64(200),
			Type:    types.WorkerInferenceRewardType,
		},
	}

	types.EmitNewInfererRewardsSettledEvent(ctx, types.BlockHeight(10), types.BlockHeight(20), rewards)

	events := ctx.EventManager().Events()
	require.Len(t, events, 1)

	event := events[0]
	require.Equal(t, "emissions.v9.EventRewardsSettled", event.Type)

	attributes := event.Attributes
	require.Len(t, attributes, 6)

	val, exists := event.GetAttribute(AttributeKeyActorType)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "INFERER")

	val, exists = event.GetAttribute(AttributeKeyTopicId)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "1")

	val, exists = event.GetAttribute(AttributeKeyBlockHeight)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "10")

	val, exists = event.GetAttribute(AttributeKeyAddresses)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), `["address1","address2"]`)

	val, exists = event.GetAttribute(AttributeKeyRewards)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), `["100","200"]`)
}

func TestEmitNewInfererRewardsSettledEventWithNoRewards(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	rewards := []types.TaskReward{}

	types.EmitNewInfererRewardsSettledEvent(ctx, types.BlockHeight(10), types.BlockHeight(20), rewards)

	events := ctx.EventManager().Events()
	require.Empty(t, events)
}

func TestEmitNewForecasterRewardsSettledEventWithRewards(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	rewards := []types.TaskReward{
		{
			TopicId: uint64(1),
			Address: "address1",
			Reward:  alloraMath.NewDecFromInt64(100),
			Type:    types.WorkerForecastRewardType,
		},
		{
			TopicId: uint64(1),
			Address: "address2",
			Reward:  alloraMath.NewDecFromInt64(200),
			Type:    types.WorkerForecastRewardType,
		},
	}

	types.EmitNewForecasterRewardsSettledEvent(ctx, types.BlockHeight(10), types.BlockHeight(20), rewards)

	events := ctx.EventManager().Events()
	require.Len(t, events, 1)

	event := events[0]
	require.Equal(t, "emissions.v9.EventRewardsSettled", event.Type)

	attributes := event.Attributes
	require.Len(t, attributes, 6)

	val, exists := event.GetAttribute(AttributeKeyActorType)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "FORECASTER")

	val, exists = event.GetAttribute(AttributeKeyTopicId)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "1")

	val, exists = event.GetAttribute(AttributeKeyBlockHeight)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "10")

	val, exists = event.GetAttribute(AttributeKeyAddresses)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), `["address1","address2"]`)

	val, exists = event.GetAttribute(AttributeKeyRewards)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), `["100","200"]`)
}

func TestEmitNewForecasterRewardsSettledEventWithNoRewards(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	rewards := []types.TaskReward{}

	types.EmitNewForecasterRewardsSettledEvent(ctx, types.BlockHeight(10), types.BlockHeight(20), rewards)

	events := ctx.EventManager().Events()
	require.Empty(t, events)
}

func TestEmitNewReputerAndDelegatorRewardsSettledEventWithRewards(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	rewards := []types.TaskReward{
		{
			TopicId: uint64(1),
			Address: "address1",
			Reward:  alloraMath.NewDecFromInt64(100),
			Type:    types.ReputerAndDelegatorRewardType,
		},
		{
			TopicId: uint64(1),
			Address: "address2",
			Reward:  alloraMath.NewDecFromInt64(200),
			Type:    types.ReputerAndDelegatorRewardType,
		},
	}

	types.EmitNewReputerAndDelegatorRewardsSettledEvent(ctx, types.BlockHeight(10), types.BlockHeight(20), rewards)

	events := ctx.EventManager().Events()
	require.Len(t, events, 1)

	event := events[0]
	require.Equal(t, "emissions.v9.EventRewardsSettled", event.Type)

	attributes := event.Attributes
	require.Len(t, attributes, 6)

	val, exists := event.GetAttribute(AttributeKeyActorType)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "REPUTER")

	val, exists = event.GetAttribute(AttributeKeyTopicId)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "1")

	val, exists = event.GetAttribute(AttributeKeyBlockHeight)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "10")

	val, exists = event.GetAttribute(AttributeKeyBlockHeightTx)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "20")

	val, exists = event.GetAttribute(AttributeKeyAddresses)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), `["address1","address2"]`)

	val, exists = event.GetAttribute(AttributeKeyRewards)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), `["100","200"]`)
}

func TestEmitNewReputerAndDelegatorRewardsSettledEventWithNoRewards(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	rewards := []types.TaskReward{}

	types.EmitNewReputerAndDelegatorRewardsSettledEvent(ctx, types.BlockHeight(10), types.BlockHeight(20), rewards)

	events := ctx.EventManager().Events()
	require.Empty(t, events)
}

func TestEmitNewNetworkLossSetEvent(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	topicId := uint64(1)
	blockHeight := int64(10)
	loss := types.ValueBundle{
		TopicId:                topicId,
		ReputerRequestNonce:    &types.ReputerRequestNonce{ReputerNonce: &types.Nonce{BlockHeight: blockHeight}},
		Reputer:                "TestReputer",
		ExtraData:              nil,
		CombinedValue:          alloraMath.MustNewDecFromString("10"),
		NaiveValue:             alloraMath.MustNewDecFromString("20"),
		InfererValues:          []*types.WorkerAttributedValue{{Worker: "TestInferer", Value: alloraMath.MustNewDecFromString("0.0112")}, {Worker: "TestInferer1", Value: alloraMath.MustNewDecFromString("0.0112")}},
		ForecasterValues:       []*types.WorkerAttributedValue{{Worker: "TestForecaster", Value: alloraMath.MustNewDecFromString("0.0112")}, {Worker: "TestForecaster1", Value: alloraMath.MustNewDecFromString("0.0112")}},
		OneOutInfererValues:    []*types.WithheldWorkerAttributedValue{{Worker: "TestInferer2", Value: alloraMath.MustNewDecFromString("0.0112")}, {Worker: "TestInferer3", Value: alloraMath.MustNewDecFromString("0.0112")}},
		OneOutForecasterValues: []*types.WithheldWorkerAttributedValue{{Worker: "TestForecaster3", Value: alloraMath.MustNewDecFromString("0.0112")}, {Worker: "TestForecaster4", Value: alloraMath.MustNewDecFromString("0.0112")}},
		OneInForecasterValues:  []*types.WorkerAttributedValue{{Worker: "TestForecaster5", Value: alloraMath.MustNewDecFromString("0.0112")}, {Worker: "TestForecaster6", Value: alloraMath.MustNewDecFromString("0.0112")}},
		OneOutInfererForecasterValues: []*types.OneOutInfererForecasterValues{
			{
				Forecaster: "allo1e7l2cyn29c8jmama9hs0n3weq4tg6pqdzsl249",
				OneOutInfererValues: []*types.WithheldWorkerAttributedValue{
					{
						Worker: "allo1e7l2cyn29c8jmama9hs0n3weq4tg6pqdzsl249",
						Value:  alloraMath.MustNewDecFromString("0.0112"),
					},
				},
			},
		},
	}

	types.EmitNewNetworkLossSetEvent(ctx, loss)

	events := ctx.EventManager().Events()
	require.Len(t, events, 1)

	event := events[0]
	require.Equal(t, "emissions.v9.EventNetworkLossSet", event.Type)

	attributes := event.Attributes
	require.Len(t, attributes, 5)
	val, exists := event.GetAttribute(AttributeKeyValueBundle)
	require.True(t, exists)
	assertEventValueBundle(t, val.GetValue(), loss)
}

func TestEmitNewNetworkInferencesEvent(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	topicId := uint64(1)
	blockHeight := int64(10)
	networkInferences := types.ValueBundle{
		TopicId:                topicId,
		ReputerRequestNonce:    &types.ReputerRequestNonce{ReputerNonce: &types.Nonce{BlockHeight: blockHeight}},
		Reputer:                "allo1e7l2cyn29c8jmama9hs0n3weq4tg6pqdzsl249",
		ExtraData:              nil,
		CombinedValue:          alloraMath.MustNewDecFromString("10"),
		NaiveValue:             alloraMath.MustNewDecFromString("20"),
		InfererValues:          []*types.WorkerAttributedValue{{Worker: "allo1e7l2cyn29c8jmama9hs0n3weq4tg6pqdzsl249", Value: alloraMath.MustNewDecFromString("0.0112")}, {Worker: "allo1xqxnuvvegql2n4xtx52n3fay4t5gkv3wr8etca", Value: alloraMath.MustNewDecFromString("0.0112")}},
		ForecasterValues:       []*types.WorkerAttributedValue{{Worker: "allo1e7l2cyn29c8jmama9hs0n3weq4tg6pqdzsl249", Value: alloraMath.MustNewDecFromString("0.0112")}, {Worker: "allo1xqxnuvvegql2n4xtx52n3fay4t5gkv3wr8etca", Value: alloraMath.MustNewDecFromString("0.0112")}},
		OneOutInfererValues:    []*types.WithheldWorkerAttributedValue{{Worker: "allo1e7l2cyn29c8jmama9hs0n3weq4tg6pqdzsl249", Value: alloraMath.MustNewDecFromString("0.0112")}, {Worker: "allo1xqxnuvvegql2n4xtx52n3fay4t5gkv3wr8etca", Value: alloraMath.MustNewDecFromString("0.0112")}},
		OneOutForecasterValues: []*types.WithheldWorkerAttributedValue{{Worker: "allo1e7l2cyn29c8jmama9hs0n3weq4tg6pqdzsl249", Value: alloraMath.MustNewDecFromString("0.0112")}, {Worker: "allo1xqxnuvvegql2n4xtx52n3fay4t5gkv3wr8etca", Value: alloraMath.MustNewDecFromString("0.0112")}},
		OneInForecasterValues:  []*types.WorkerAttributedValue{{Worker: "allo1e7l2cyn29c8jmama9hs0n3weq4tg6pqdzsl249", Value: alloraMath.MustNewDecFromString("0.0112")}, {Worker: "allo1xqxnuvvegql2n4xtx52n3fay4t5gkv3wr8etca", Value: alloraMath.MustNewDecFromString("0.0112")}},
		OneOutInfererForecasterValues: []*types.OneOutInfererForecasterValues{
			{
				Forecaster: "allo1e7l2cyn29c8jmama9hs0n3weq4tg6pqdzsl249",
				OneOutInfererValues: []*types.WithheldWorkerAttributedValue{
					{
						Worker: "allo1e7l2cyn29c8jmama9hs0n3weq4tg6pqdzsl249",
						Value:  alloraMath.MustNewDecFromString("0.0112"),
					},
				},
			},
		},
	}

	types.EmitNewNetworkInferencesEvent(ctx, networkInferences)

	events := ctx.EventManager().Events()
	require.Len(t, events, 1)

	event := events[0]
	require.Equal(t, "emissions.v9.EventNetworkInferences", event.Type)

	attributes := event.Attributes
	require.Len(t, attributes, 5)

	val, exists := event.GetAttribute(AttributeKeyValueBundle)
	require.True(t, exists)
	assertEventValueBundle(t, val.GetValue(), networkInferences)
}

func assertEventValueBundle(t *testing.T, val string, bundle types.ValueBundle) {
	t.Helper()
	var result types.EventValueBundle
	_ = json.Unmarshal([]byte(val), &result)
	require.Equal(t, bundle.CombinedValue, result.CombinedValue)
	require.Equal(t, bundle.NaiveValue, result.NaiveValue)
	require.Equal(t, len(bundle.InfererValues), len(result.InfererValues))
	for i := range bundle.InfererValues {
		require.Equal(t, bundle.InfererValues[i].Value, result.InfererValues[i])
	}
	require.Equal(t, len(bundle.ForecasterValues), len(result.ForecasterValues))
	for i := range bundle.ForecasterValues {
		require.Equal(t, bundle.ForecasterValues[i].Value, result.ForecasterValues[i])
	}
	require.Equal(t, len(bundle.OneOutInfererValues), len(result.OneOutInfererValues))
	for i := range bundle.OneOutInfererValues {
		require.Equal(t, bundle.OneOutInfererValues[i].Value, result.OneOutInfererValues[i])
	}
	require.Equal(t, len(bundle.OneOutForecasterValues), len(result.OneOutForecasterValues))
	for i := range bundle.OneOutForecasterValues {
		require.Equal(t, bundle.OneOutForecasterValues[i].Value, result.OneOutForecasterValues[i])
	}
	require.Equal(t, len(bundle.OneInForecasterValues), len(result.OneInForecasterValues))
	for i := range bundle.OneInForecasterValues {
		require.Equal(t, bundle.OneInForecasterValues[i].Value, result.OneInForecasterValues[i])
	}
}

func TestEmitNewForecastTaskSetEvent(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	topicId := uint64(1)
	nonce := int64(10)
	CombinedValue := alloraMath.MustNewDecFromString("10")
	NaiveValue := alloraMath.MustNewDecFromString("20")

	score, err := NaiveValue.Sub(CombinedValue)
	require.NoError(t, err)

	types.EmitNewForecastTaskUtilityScoreSetEvent(ctx, topicId, score, nonce)

	events := ctx.EventManager().Events()
	require.Len(t, events, 1)

	event := events[0]
	require.Equal(t, "emissions.v9.EventForecastTaskScoreSet", event.Type)

	// Check that we have the expected number of attributes
	require.Len(t, event.Attributes, 3)

	// Create a map to check attributes regardless of order
	attributes := make(map[string]string)
	for _, attr := range event.Attributes {
		attributes[attr.Key] = attr.Value
	}

	// Verify all expected attributes are present with correct values
	require.Contains(t, attributes, "topic_id")
	require.Equal(t, "\"1\"", attributes["topic_id"])

	require.Contains(t, attributes, "score")
	require.Equal(t, "\"10\"", attributes["score"])

	require.Contains(t, attributes, "nonce_block_height")
	require.Equal(t, "\"10\"", attributes["nonce_block_height"])
}

func TestNewLastCommitSetEvent(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	topicId1 := uint64(1)
	topicId2 := uint64(2)
	workerHeight := int64(10)
	worker2Height := int64(20)
	reputerHeight := int64(30)
	types.EmitNewWorkerLastCommitSetEvent(ctx, topicId1, workerHeight, &types.Nonce{BlockHeight: workerHeight - 5})
	types.EmitNewWorkerLastCommitSetEvent(ctx, topicId1, worker2Height, &types.Nonce{BlockHeight: worker2Height - 5})
	types.EmitNewReputerLastCommitSetEvent(ctx, topicId2, reputerHeight, &types.Nonce{BlockHeight: reputerHeight - 5})

	events := ctx.EventManager().Events()
	require.Len(t, events, 3)

	require.Equal(t, "emissions.v9.EventWorkerLastCommitSet", events[0].Type)
	require.Equal(t, "emissions.v9.EventWorkerLastCommitSet", events[1].Type)
	require.Equal(t, "emissions.v9.EventReputerLastCommitSet", events[2].Type)

	require.Contains(t, events[0].Attributes[0].Key, "block_height")
	require.Contains(t, events[0].Attributes[1].Key, "nonce")
	require.Contains(t, events[0].Attributes[2].Key, "topic_id")
	require.Contains(t, events[0].Attributes[0].Value, "10")
	require.Contains(t, events[0].Attributes[1].Value, "{\"block_height\":\"5\"}")
	require.Contains(t, events[0].Attributes[2].Value, "1")

	require.Contains(t, events[1].Attributes[0].Key, "block_height")
	require.Contains(t, events[1].Attributes[1].Key, "nonce")
	require.Contains(t, events[1].Attributes[2].Key, "topic_id")
	require.Contains(t, events[1].Attributes[0].Value, "20")
	require.Contains(t, events[1].Attributes[1].Value, "{\"block_height\":\"15\"}")
	require.Contains(t, events[1].Attributes[2].Value, "1")

	require.Contains(t, events[2].Attributes[0].Key, "block_height")
	require.Contains(t, events[2].Attributes[1].Key, "nonce")
	require.Contains(t, events[2].Attributes[2].Key, "topic_id")
	require.Contains(t, events[2].Attributes[0].Value, "30")
	require.Contains(t, events[2].Attributes[1].Value, "{\"block_height\":\"25\"}")
	require.Contains(t, events[2].Attributes[2].Value, "2")
}

func TestEmitNewTopicRewardsSetEvent(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	var topicIds = []uint64{1, 2, 3, 4, 5}
	topicRewards := make(map[uint64]*alloraMath.Dec)
	for index, id := range topicIds {
		reward := alloraMath.MustNewDecFromString(strconv.Itoa(10 * index))
		topicRewards[id] = &reward
	}

	types.EmitNewTopicRewardSetEvent(ctx, topicRewards)
	events := ctx.EventManager().Events()
	require.Len(t, events, 1)

	require.Equal(t, "emissions.v9.EventTopicRewardsSet", events[0].Type)
	require.Contains(t, events[0].Attributes[0].Key, "rewards")
	require.Contains(t, events[0].Attributes[0].Value, `["0","10","20","30","40"]`)
	require.Contains(t, events[0].Attributes[1].Key, "topic_ids")
	require.Contains(t, events[0].Attributes[1].Value, `["1","2","3","4","5"]`)
}

func TestEmitNewEMAScoresSetEventWithScores(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	activeArr := make(map[string]bool)
	emaScores := []types.Score{
		{
			TopicId:     uint64(1),
			BlockHeight: int64(10),
			Address:     "address1",
			Score:       alloraMath.NewDecFromInt64(100),
		},
		{
			TopicId:     uint64(1),
			BlockHeight: int64(10),
			Address:     "address2",
			Score:       alloraMath.NewDecFromInt64(200),
		},
	}

	activeArr[emaScores[0].Address] = true
	activeArr[emaScores[1].Address] = false
	types.EmitNewActorEMAScoresSetEvent(ctx, types.ActorType_ACTOR_TYPE_INFERER_UNSPECIFIED, int64(10), emaScores, activeArr)
	activeArr[emaScores[0].Address] = false
	activeArr[emaScores[1].Address] = false
	types.EmitNewActorEMAScoresSetEvent(ctx, types.ActorType_ACTOR_TYPE_FORECASTER, int64(10), emaScores, activeArr)
	activeArr[emaScores[0].Address] = true
	activeArr[emaScores[1].Address] = true
	types.EmitNewActorEMAScoresSetEvent(ctx, types.ActorType_ACTOR_TYPE_REPUTER, int64(10), emaScores, activeArr)

	events := ctx.EventManager().Events()
	require.Len(t, events, 3)

	event := events[0]
	require.Equal(t, "emissions.v9.EventEMAScoresSet", event.Type)

	require.Contains(t, events[0].Attributes[0].Key, "actor_type")
	require.Contains(t, events[0].Attributes[0].Value, "\"ACTOR_TYPE_INFERER_UNSPECIFIED\"")
	require.Contains(t, events[0].Attributes[1].Key, "addresses")
	require.Contains(t, events[0].Attributes[1].Value, "[\"address1\",\"address2\"]")
	require.Contains(t, events[0].Attributes[2].Key, "is_active")
	require.Contains(t, events[0].Attributes[2].Value, "[true,false]")
	require.Contains(t, events[0].Attributes[3].Key, "nonce")
	require.Contains(t, events[0].Attributes[3].Value, "\"10\"")
	require.Contains(t, events[0].Attributes[4].Key, "scores")
	require.Contains(t, events[0].Attributes[4].Value, "[\"100\",\"200\"]")
	require.Contains(t, events[0].Attributes[5].Key, "topic_id")
	require.Contains(t, events[0].Attributes[5].Value, "\"1\"")

	require.Contains(t, events[1].Attributes[0].Key, "actor_type")
	require.Contains(t, events[1].Attributes[0].Value, "\"ACTOR_TYPE_FORECASTER\"")
	require.Contains(t, events[1].Attributes[2].Key, "is_active")
	require.Contains(t, events[1].Attributes[2].Value, "[false,false]")

	require.Contains(t, events[2].Attributes[0].Key, "actor_type")
	require.Contains(t, events[2].Attributes[0].Value, "\"ACTOR_TYPE_REPUTER\"")
	require.Contains(t, events[2].Attributes[2].Key, "is_active")
	require.Contains(t, events[2].Attributes[2].Value, "[true,true]")
}

func TestEmitNewListeningCoefficientsSetEvent(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())

	actorType := types.ActorType_ACTOR_TYPE_REPUTER
	topicID := uint64(1)
	blockHeight := int64(10)
	addresses := []string{"address1", "address2"}
	coefficients := []alloraMath.Dec{alloraMath.NewDecFromInt64(100), alloraMath.NewDecFromInt64(200)}

	types.EmitNewListeningCoefficientsSetEvent(ctx, actorType, topicID, blockHeight, addresses, coefficients)

	events := ctx.EventManager().Events()
	require.Len(t, events, 1)

	event := events[0]
	require.Equal(t, "emissions.v9.EventListeningCoefficientsSet", event.Type)

	attributes := event.Attributes
	require.Len(t, attributes, 5)

	val, exists := event.GetAttribute(AttributeKeyActorType)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "ACTOR_TYPE_REPUTER")

	val, exists = event.GetAttribute(AttributeKeyTopicId)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "1")

	val, exists = event.GetAttribute(AttributeKeyBlockHeight)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "10")

	val, exists = event.GetAttribute(AttributeKeyAddresses)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), `["address1","address2"]`)

	val, exists = event.GetAttribute(AttributeKeyCoefficients)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), `["100","200"]`)
}

func TestEmitNewReputerScoresSetEventWithNoAddresses(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	actorType := types.ActorType_ACTOR_TYPE_REPUTER
	topicID := uint64(1)
	blockHeight := int64(10)
	addresses := []string{}
	coefficients := []alloraMath.Dec{}

	types.EmitNewListeningCoefficientsSetEvent(ctx, actorType, topicID, blockHeight, addresses, coefficients)

	events := ctx.EventManager().Events()
	require.Empty(t, events)
}

func TestEmitNewReputerScoresSetEventWithNoCoefficients(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	actorType := types.ActorType_ACTOR_TYPE_REPUTER
	topicID := uint64(1)
	blockHeight := int64(10)
	addresses := []string{"address1", "address2"}
	coefficients := []alloraMath.Dec{}

	types.EmitNewListeningCoefficientsSetEvent(ctx, actorType, topicID, blockHeight, addresses, coefficients)

	events := ctx.EventManager().Events()
	require.Empty(t, events)
}

func TestEmitNewInfererNetworkRegretSetEvent(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	topicID := uint64(1)
	blockHeight := int64(10)
	addresses := []string{"address1", "address2"}
	regrets := []alloraMath.Dec{alloraMath.NewDecFromInt64(100), alloraMath.NewDecFromInt64(200)}

	types.EmitNewInfererNetworkRegretSetEvent(ctx, topicID, blockHeight, addresses, regrets)

	events := ctx.EventManager().Events()
	require.Len(t, events, 1)

	event := events[0]
	require.Equal(t, "emissions.v9.EventInfererNetworkRegretSet", event.Type)

	val, exists := event.GetAttribute(AttributeKeyTopicId)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "1")

	val, exists = event.GetAttribute(AttributeKeyBlockHeight)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "10")

	val, exists = event.GetAttribute(AttributeKeyAddresses)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), `["address1","address2"]`)

	val, exists = event.GetAttribute(AttributeKeyRegrets)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), `["100","200"]`)
}

func TestEmitNewInfererNetworkRegretSetEventWithNoAddresses(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	topicID := uint64(1)
	blockHeight := int64(10)
	addresses := []string{}
	regrets := []alloraMath.Dec{alloraMath.NewDecFromInt64(100), alloraMath.NewDecFromInt64(200)}

	types.EmitNewInfererNetworkRegretSetEvent(ctx, topicID, blockHeight, addresses, regrets)
	events := ctx.EventManager().Events()
	require.Empty(t, events)
}

func TestEmitNewInfererNetworkRegretSetEventWithNoRegrets(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	topicID := uint64(1)
	blockHeight := int64(10)
	addresses := []string{"address1", "address2"}
	regrets := []alloraMath.Dec{}

	types.EmitNewInfererNetworkRegretSetEvent(ctx, topicID, blockHeight, addresses, regrets)

	events := ctx.EventManager().Events()
	require.Empty(t, events)
}

func TestEmitNewForecasterNetworkRegretSetEvent(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	topicID := uint64(1)
	blockHeight := int64(10)
	addresses := []string{"address1", "address2"}
	regrets := []alloraMath.Dec{alloraMath.NewDecFromInt64(100), alloraMath.NewDecFromInt64(200)}

	types.EmitNewForecasterNetworkRegretSetEvent(ctx, topicID, blockHeight, addresses, regrets)

	events := ctx.EventManager().Events()
	require.Len(t, events, 1)

	event := events[0]
	require.Equal(t, "emissions.v9.EventForecasterNetworkRegretSet", event.Type)

	val, exists := event.GetAttribute(AttributeKeyTopicId)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "1")

	val, exists = event.GetAttribute(AttributeKeyBlockHeight)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "10")

	val, exists = event.GetAttribute(AttributeKeyAddresses)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), `["address1","address2"]`)

	val, exists = event.GetAttribute(AttributeKeyRegrets)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), `["100","200"]`)
}

func TestEmitNewForecasterNetworkRegretSetEventWithNoAddresses(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	topicID := uint64(1)
	blockHeight := int64(10)
	addresses := []string{}
	regrets := []alloraMath.Dec{alloraMath.NewDecFromInt64(100), alloraMath.NewDecFromInt64(200)}

	types.EmitNewForecasterNetworkRegretSetEvent(ctx, topicID, blockHeight, addresses, regrets)
	events := ctx.EventManager().Events()
	require.Empty(t, events)
}

func TestEmitNewForecasterNetworkRegretSetEventWithNoRegrets(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	topicID := uint64(1)
	blockHeight := int64(10)
	addresses := []string{"address1", "address2"}
	regrets := []alloraMath.Dec{}

	types.EmitNewForecasterNetworkRegretSetEvent(ctx, topicID, blockHeight, addresses, regrets)
	events := ctx.EventManager().Events()
	require.Empty(t, events)
}

func TestEmitNewNaiveInfererNetworkRegretSetEvent(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	topicID := uint64(1)
	blockHeight := int64(10)
	addresses := []string{"address1", "address2"}
	regrets := []alloraMath.Dec{alloraMath.NewDecFromInt64(100), alloraMath.NewDecFromInt64(200)}

	types.EmitNewNaiveInfererNetworkRegretSetEvent(ctx, topicID, blockHeight, addresses, regrets)

	events := ctx.EventManager().Events()
	require.Len(t, events, 1)

	event := events[0]
	require.Equal(t, "emissions.v9.EventNaiveInfererNetworkRegretSet", event.Type)

	val, exists := event.GetAttribute(AttributeKeyTopicId)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "1")

	val, exists = event.GetAttribute(AttributeKeyBlockHeight)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "10")

	val, exists = event.GetAttribute(AttributeKeyAddresses)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), `["address1","address2"]`)

	val, exists = event.GetAttribute(AttributeKeyRegrets)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), `["100","200"]`)
}

func TestEmitNewNaiveInfererNetworkRegretSetEventWithNoAddresses(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	topicID := uint64(1)
	blockHeight := int64(10)
	addresses := []string{}
	regrets := []alloraMath.Dec{alloraMath.NewDecFromInt64(100), alloraMath.NewDecFromInt64(200)}

	types.EmitNewNaiveInfererNetworkRegretSetEvent(ctx, topicID, blockHeight, addresses, regrets)
	events := ctx.EventManager().Events()
	require.Empty(t, events)
}

func TestEmitNewNaiveInfererNetworkRegretSetEventWithNoRegrets(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	topicID := uint64(1)
	blockHeight := int64(10)
	addresses := []string{"address1", "address2"}
	regrets := []alloraMath.Dec{}

	types.EmitNewNaiveInfererNetworkRegretSetEvent(ctx, topicID, blockHeight, addresses, regrets)
	events := ctx.EventManager().Events()
	require.Empty(t, events)
}

func TestEmitNewTopicInitialRegretSetEvent(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	topicID := uint64(1)
	blockHeight := int64(10)
	regret := alloraMath.NewDecFromInt64(100)

	types.EmitNewTopicInitialRegretSetEvent(ctx, topicID, blockHeight, regret)

	events := ctx.EventManager().Events()
	require.Len(t, events, 1)

	event := events[0]
	require.Equal(t, "emissions.v9.EventTopicInitialRegretSet", event.Type)

	val, exists := event.GetAttribute(AttributeKeyTopicId)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "1")

	val, exists = event.GetAttribute(AttributeKeyBlockHeight)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "10")

	val, exists = event.GetAttribute(AttributeKeyRegret)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "100")
}

func TestEmitPreviousPercentageRewardToStakedReputersSetEvent(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	blockHeight := int64(10)
	percentage := alloraMath.MustNewDecFromString("0.75")

	types.EmitNewPreviousPercentageRewardToStakedReputersSetEvent(ctx, blockHeight, percentage)

	events := ctx.EventManager().Events()
	require.Len(t, events, 1)

	event := events[0]
	require.Equal(t, "emissions.v9.EventPreviousPercentageRewardToStakedReputersSet", event.Type)

	val, exists := event.GetAttribute(AttributeKeyBlockHeight)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), strconv.FormatInt(blockHeight, 10))

	val, exists = event.GetAttribute("percentage")
	require.True(t, exists)
	require.Contains(t, val.GetValue(), percentage.String())
}

func TestEmitNewInfererWeightsSetEvent(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	topicId := uint64(1)
	blockHeight := int64(10)
	addresses := []string{"address1", "address2"}
	weights := []alloraMath.Dec{alloraMath.MustNewDecFromString("0.5"), alloraMath.MustNewDecFromString("0.3")}

	types.EmitNewInfererWeightsSetEvent(ctx, topicId, blockHeight, addresses, weights)

	events := ctx.EventManager().Events()
	require.Len(t, events, 1)

	event := events[0]
	require.Equal(t, "emissions.v9.EventInfererWeightsSet", event.Type)

	val, exists := event.GetAttribute(AttributeKeyTopicId)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "1")

	val, exists = event.GetAttribute(AttributeKeyBlockHeight)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "10")

	val, exists = event.GetAttribute(AttributeKeyAddresses)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), `["address1","address2"]`)

	val, exists = event.GetAttribute(AttributeKeyWeights)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), `["0.5","0.3"]`)
}

func TestEmitNewInfererWeightsSetEventWithNoAddresses(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	topicId := uint64(1)
	blockHeight := int64(10)
	addresses := []string{}
	weights := []alloraMath.Dec{alloraMath.MustNewDecFromString("0.5"), alloraMath.MustNewDecFromString("0.3")}

	types.EmitNewInfererWeightsSetEvent(ctx, topicId, blockHeight, addresses, weights)
	events := ctx.EventManager().Events()
	require.Empty(t, events)
}

func TestEmitNewInfererWeightsSetEventWithNoWeights(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	topicId := uint64(1)
	blockHeight := int64(10)
	addresses := []string{"address1", "address2"}
	weights := []alloraMath.Dec{}

	types.EmitNewInfererWeightsSetEvent(ctx, topicId, blockHeight, addresses, weights)
	events := ctx.EventManager().Events()
	require.Empty(t, events)
}

func TestEmitNewForecasterWeightsSetEvent(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	topicId := uint64(1)
	blockHeight := int64(10)
	addresses := []string{"address1", "address2"}
	weights := []alloraMath.Dec{alloraMath.MustNewDecFromString("0.5"), alloraMath.MustNewDecFromString("0.3")}

	types.EmitNewForecasterWeightsSetEvent(ctx, topicId, blockHeight, addresses, weights)

	events := ctx.EventManager().Events()
	require.Len(t, events, 1)

	event := events[0]
	require.Equal(t, "emissions.v9.EventForecasterWeightsSet", event.Type)

	val, exists := event.GetAttribute(AttributeKeyTopicId)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "1")

	val, exists = event.GetAttribute(AttributeKeyBlockHeight)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "10")

	val, exists = event.GetAttribute(AttributeKeyAddresses)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), `["address1","address2"]`)

	val, exists = event.GetAttribute(AttributeKeyWeights)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), `["0.5","0.3"]`)
}

func TestEmitNewForecasterWeightsSetEventWithNoAddresses(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	topicId := uint64(1)
	blockHeight := int64(10)
	addresses := []string{}
	weights := []alloraMath.Dec{alloraMath.MustNewDecFromString("0.5"), alloraMath.MustNewDecFromString("0.3")}

	types.EmitNewForecasterWeightsSetEvent(ctx, topicId, blockHeight, addresses, weights)
	events := ctx.EventManager().Events()
	require.Empty(t, events)
}

func TestEmitNewForecasterWeightsSetEventWithNoWeights(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	topicId := uint64(1)
	blockHeight := int64(10)
	addresses := []string{"address1", "address2"}
	weights := []alloraMath.Dec{}

	types.EmitNewForecasterWeightsSetEvent(ctx, topicId, blockHeight, addresses, weights)
	events := ctx.EventManager().Events()
	require.Empty(t, events)
}

func TestEmitNewNetworkInferenceInfererWeightsSetEvent(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	topicId := uint64(1)
	blockHeight := int64(10)
	addresses := []string{"address1", "address2"}
	weights := []alloraMath.Dec{alloraMath.MustNewDecFromString("0.5"), alloraMath.MustNewDecFromString("0.3")}

	types.EmitNewNetworkInferenceInfererWeightsSetEvent(ctx, topicId, blockHeight, addresses, weights)

	events := ctx.EventManager().Events()
	require.Len(t, events, 1)

	event := events[0]
	require.Equal(t, "emissions.v9.EventNetworkInferenceInfererWeightsSet", event.Type)

	val, exists := event.GetAttribute(AttributeKeyTopicId)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "1")

	val, exists = event.GetAttribute("nonce_block_height")
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "10")

	val, exists = event.GetAttribute(AttributeKeyAddresses)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), `["address1","address2"]`)

	val, exists = event.GetAttribute(AttributeKeyWeights)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), `["0.5","0.3"]`)
}

func TestEmitNewNetworkInferenceForecasterWeightsSetEvent(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	topicId := uint64(1)
	blockHeight := int64(10)
	addresses := []string{"address1", "address2"}
	weights := []alloraMath.Dec{alloraMath.MustNewDecFromString("0.5"), alloraMath.MustNewDecFromString("0.3")}

	types.EmitNewNetworkInferenceForecasterWeightsSetEvent(ctx, topicId, blockHeight, addresses, weights)

	events := ctx.EventManager().Events()
	require.Len(t, events, 1)

	event := events[0]
	require.Equal(t, "emissions.v9.EventNetworkInferenceForecasterWeightsSet", event.Type)

	val, exists := event.GetAttribute(AttributeKeyTopicId)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "1")

	val, exists = event.GetAttribute("nonce_block_height")
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "10")

	val, exists = event.GetAttribute(AttributeKeyAddresses)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), `["address1","address2"]`)

	val, exists = event.GetAttribute(AttributeKeyWeights)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), `["0.5","0.3"]`)
}

func TestEmitNewTopicFeeRevenueDrippedEvent(t *testing.T) {
	ctx := sdk.Context{}.WithEventManager(sdk.NewEventManager())
	topicId := uint64(1)
	oldRevenue := cosmosMath.NewInt(1000)
	newRevenue := cosmosMath.NewInt(950)
	dripAmount := cosmosMath.NewInt(50)

	types.EmitNewTopicFeeRevenueDrippedEvent(ctx, topicId, oldRevenue, newRevenue, dripAmount)

	events := ctx.EventManager().Events()
	require.Len(t, events, 1)

	event := events[0]
	require.Equal(t, "emissions.v9.EventTopicFeeRevenueDripped", event.Type)

	val, exists := event.GetAttribute(AttributeKeyTopicId)
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "1")

	val, exists = event.GetAttribute("old_revenue")
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "1000")

	val, exists = event.GetAttribute("new_revenue")
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "950")

	val, exists = event.GetAttribute("drip_amount")
	require.True(t, exists)
	require.Contains(t, val.GetValue(), "50")
}
