diff --git a/testutil/apptesting/events.go b/testutil/apptesting/events.go index f06410fc2..0df78a15d 100644 --- a/testutil/apptesting/events.go +++ b/testutil/apptesting/events.go @@ -65,6 +65,18 @@ func (s *KeeperTestHelper) AssertNEventValuesEmitted(eventValue string, nEvents s.Equal(nEvents, emissions, "Expected %v events, got %v", nEvents, emissions) } +func (s *KeeperTestHelper) GetAllMatchingEvents(eventValue string) (events []sdk.Event) { + allEvents := s.Ctx.EventManager().Events() + for _, event := range allEvents { + for _, attr := range event.Attributes { + if attr.Value == eventValue { + events = append(events, event) + } + } + } + return events +} + func (s *KeeperTestHelper) AssertEventValueNotEmitted(eventValue, message string) { allEvents := s.Ctx.EventManager().Events() if len(allEvents) != 0 { diff --git a/x/dex/keeper/limit_order_tranche.go b/x/dex/keeper/limit_order_tranche.go index 2c0dcae57..eeffcfd8a 100644 --- a/x/dex/keeper/limit_order_tranche.go +++ b/x/dex/keeper/limit_order_tranche.go @@ -56,7 +56,7 @@ func (k Keeper) FindLimitOrderTranche( // UpdateTranche handles the logic for all updates to active LimitOrderTranches in the KV Store. // NOTE: This method should always be called even if not all logic branches are applicable. // It avoids unnecessary repetition of logic and provides a single place to attach update event handlers. -func (k Keeper) UpdateTranche(ctx sdk.Context, tranche *types.LimitOrderTranche) { +func (k Keeper) UpdateTranche(ctx sdk.Context, tranche *types.LimitOrderTranche, swapMetadata ...types.SwapMetadata) { switch { // Tranche still has TokenIn (ReservesMakerDenom) ==> Just save the update @@ -77,7 +77,7 @@ func (k Keeper) UpdateTranche(ctx sdk.Context, tranche *types.LimitOrderTranche) ctx.EventManager().EmitEvents(types.GetEventsDecTotalOrders(tranche.Key.TradePairId)) } - ctx.EventManager().EmitEvent(types.CreateTickUpdateLimitOrderTranche(tranche)) + ctx.EventManager().EmitEvent(types.CreateTickUpdateLimitOrderTranche(tranche, swapMetadata...)) } func (k Keeper) SetLimitOrderTranche(ctx sdk.Context, tranche *types.LimitOrderTranche) { diff --git a/x/dex/keeper/liquidity.go b/x/dex/keeper/liquidity.go index 87de37680..a0d74497d 100644 --- a/x/dex/keeper/liquidity.go +++ b/x/dex/keeper/liquidity.go @@ -43,7 +43,12 @@ func (k Keeper) Swap( inAmount, outAmount := liq.Swap(remainingTakerDenom, remainingMakerDenom) - k.SaveLiquidity(ctx, liq) + swapMetadata := types.SwapMetadata{ + AmountIn: inAmount, + AmountOut: outAmount, + TokenIn: tradePairID.TakerDenom, + } + k.SaveLiquidity(ctx, liq, swapMetadata) remainingTakerDenom = remainingTakerDenom.Sub(inAmount) totalMakerDenom = totalMakerDenom.Add(outAmount) @@ -107,14 +112,14 @@ func (k Keeper) SwapWithCache( return totalIn, totalOut, orderFilled, err } -func (k Keeper) SaveLiquidity(sdkCtx sdk.Context, liquidityI types.Liquidity) { +func (k Keeper) SaveLiquidity(sdkCtx sdk.Context, liquidityI types.Liquidity, swapMetadata ...types.SwapMetadata) { switch liquidity := liquidityI.(type) { case *types.LimitOrderTranche: // If there is still makerReserves we will save the tranche as active, if not, we will move it to inactive - k.UpdateTranche(sdkCtx, liquidity) + k.UpdateTranche(sdkCtx, liquidity, swapMetadata...) case *types.PoolLiquidity: // Save updated to both sides of the pool. If one of the sides is empty it will be deleted - k.UpdatePool(sdkCtx, liquidity.Pool) + k.UpdatePool(sdkCtx, liquidity.Pool, swapMetadata...) default: panic("Invalid liquidity type") } diff --git a/x/dex/keeper/liquidity_test.go b/x/dex/keeper/liquidity_test.go index ce08905ba..7f3348f19 100644 --- a/x/dex/keeper/liquidity_test.go +++ b/x/dex/keeper/liquidity_test.go @@ -547,6 +547,29 @@ func (s *DexTestSuite) TestSwapExhaustsLOAndLP() { // There should be total of 6 tick updates // (limitOrder, 2x deposit, 2x swap LP, swap LO) s.AssertNEventValuesEmitted(types.TickUpdateEventKey, 6) + + tickUpdates := s.GetAllMatchingEvents(types.TickUpdateEventKey) + + // LimitOrder TickUpdate has correct SwapMetadatrra + loTickUpdate := tickUpdates[3] + loSwapIn, _ := loTickUpdate.GetAttribute(types.AttributeSwapAmountIn) + loSwapOut, _ := loTickUpdate.GetAttribute(types.AttributeSwapAmountOut) + s.Equal("10000000", loSwapIn.Value) + s.Equal("10000000", loSwapOut.Value) + + // LP TickUpdate has correct SwapMetadata + lpTickUpdate := tickUpdates[5] + lpSwapIn, _ := lpTickUpdate.GetAttribute(types.AttributeSwapAmountIn) + lpSwapOut, _ := lpTickUpdate.GetAttribute(types.AttributeSwapAmountOut) + s.Equal("9000000", lpSwapIn.Value) + s.Equal("8999100", lpSwapOut.Value) + + // opposite LP TickUpdate has no SwapMetadata + lpTickUpdateOppositeTick := tickUpdates[4] + _, found := lpTickUpdateOppositeTick.GetAttribute(types.AttributeSwapAmountIn) + s.False(found) + _, found = lpTickUpdateOppositeTick.GetAttribute(types.AttributeSwapAmountOut) + s.False(found) } // Test helpers /////////////////////////////////////////////////////////////// diff --git a/x/dex/keeper/pool.go b/x/dex/keeper/pool.go index f4c13f6b2..2d16ea9ec 100644 --- a/x/dex/keeper/pool.go +++ b/x/dex/keeper/pool.go @@ -137,9 +137,21 @@ func (k Keeper) GetPoolIDByParams( // UpdatePool handles the logic for all updates to Pools in the KV Store. // It provides a convenient way to save both sides of the pool reserves. -func (k Keeper) UpdatePool(ctx sdk.Context, pool *types.Pool) { - k.UpdatePoolReserves(ctx, pool.LowerTick0) - k.UpdatePoolReserves(ctx, pool.UpperTick1) +func (k Keeper) UpdatePool(ctx sdk.Context, pool *types.Pool, swapMetadata ...types.SwapMetadata) { + if len(swapMetadata) == 1 { + // Only pass the swapMetadata to the poolReserves that is being swapped against + if swapMetadata[0].TokenIn == pool.LowerTick0.Key.TradePairId.TakerDenom { + k.UpdatePoolReserves(ctx, pool.LowerTick0, swapMetadata...) + k.UpdatePoolReserves(ctx, pool.UpperTick1) + } else { + k.UpdatePoolReserves(ctx, pool.LowerTick0) + k.UpdatePoolReserves(ctx, pool.UpperTick1, swapMetadata...) + } + } else { + k.UpdatePoolReserves(ctx, pool.LowerTick0) + k.UpdatePoolReserves(ctx, pool.UpperTick1) + + } } // GetPoolCount get the total number of pools diff --git a/x/dex/keeper/pool_reserves.go b/x/dex/keeper/pool_reserves.go index df870e21d..79af46466 100644 --- a/x/dex/keeper/pool_reserves.go +++ b/x/dex/keeper/pool_reserves.go @@ -44,7 +44,7 @@ func (k Keeper) RemovePoolReserves(ctx sdk.Context, poolReservesID *types.PoolRe // UpdatePoolReserves handles the logic for all updates to PoolReserves in the KV Store. // NOTE: This method should always be called even if not all logic branches are applicable. // It avoids unnecessary repetition of logic and provides a single place to attach update event handlers. -func (k Keeper) UpdatePoolReserves(ctx sdk.Context, reserves *types.PoolReserves) { +func (k Keeper) UpdatePoolReserves(ctx sdk.Context, reserves *types.PoolReserves, swapMetadata ...types.SwapMetadata) { if reserves.HasToken() { // The pool still has ReservesMakerDenom; save it as is k.SetPoolReserves(ctx, reserves) @@ -57,5 +57,5 @@ func (k Keeper) UpdatePoolReserves(ctx sdk.Context, reserves *types.PoolReserves // TODO: This will create a bit of extra noise since UpdatePoolReserves is called for both sides of the pool, // but not in some cases only one side has been updated // This should be solved upstream by better tracking of dirty ticks - ctx.EventManager().EmitEvent(types.CreateTickUpdatePoolReserves(*reserves)) + ctx.EventManager().EmitEvent(types.CreateTickUpdatePoolReserves(*reserves, swapMetadata...)) } diff --git a/x/dex/types/events.go b/x/dex/types/events.go index 4f96e826b..c856b7fd3 100644 --- a/x/dex/types/events.go +++ b/x/dex/types/events.go @@ -263,6 +263,21 @@ func CancelLimitOrderEvent( return sdk.NewEvent(sdk.EventTypeMessage, attrs...) } +type SwapMetadata struct { + AmountIn math.Int + AmountOut math.Int + TokenIn string +} + +func addSwapMetadata(event sdk.Event, swapMetadata SwapMetadata) sdk.Event { + swapAttrs := []sdk.Attribute{ + sdk.NewAttribute(AttributeSwapAmountIn, swapMetadata.AmountIn.String()), + sdk.NewAttribute(AttributeSwapAmountOut, swapMetadata.AmountOut.String()), + } + + return event.AppendAttributes(swapAttrs...) +} + func TickUpdateEvent( token0 string, token1 string, @@ -285,10 +300,10 @@ func TickUpdateEvent( return sdk.NewEvent(EventTypeTickUpdate, attrs...) } -func CreateTickUpdatePoolReserves(tick PoolReserves) sdk.Event { +func CreateTickUpdatePoolReserves(tick PoolReserves, swapMetadata ...SwapMetadata) sdk.Event { tradePairID := tick.Key.TradePairId pairID := tradePairID.MustPairID() - return TickUpdateEvent( + tickUpdate := TickUpdateEvent( pairID.Token0, pairID.Token1, tradePairID.MakerDenom, @@ -296,12 +311,17 @@ func CreateTickUpdatePoolReserves(tick PoolReserves) sdk.Event { tick.ReservesMakerDenom, sdk.NewAttribute(AttributeFee, strconv.FormatUint(tick.Key.Fee, 10)), ) + if len(swapMetadata) == 1 { + tickUpdate = addSwapMetadata(tickUpdate, swapMetadata[0]) + } + + return tickUpdate } -func CreateTickUpdateLimitOrderTranche(tranche *LimitOrderTranche) sdk.Event { +func CreateTickUpdateLimitOrderTranche(tranche *LimitOrderTranche, swapMetadata ...SwapMetadata) sdk.Event { tradePairID := tranche.Key.TradePairId pairID := tradePairID.MustPairID() - return TickUpdateEvent( + tickUpdate := TickUpdateEvent( pairID.Token0, pairID.Token1, tradePairID.MakerDenom, @@ -309,6 +329,12 @@ func CreateTickUpdateLimitOrderTranche(tranche *LimitOrderTranche) sdk.Event { tranche.ReservesMakerDenom, sdk.NewAttribute(AttributeTrancheKey, tranche.Key.TrancheKey), ) + + if len(swapMetadata) == 1 { + tickUpdate = addSwapMetadata(tickUpdate, swapMetadata[0]) + } + + return tickUpdate } func CreateTickUpdateLimitOrderTranchePurge(tranche *LimitOrderTranche) sdk.Event {