diff --git a/app/setup_handlers.go b/app/setup_handlers.go index 2eda2b94b..a929e7114 100644 --- a/app/setup_handlers.go +++ b/app/setup_handlers.go @@ -19,7 +19,7 @@ const ( ) // make sure to update these when you upgrade the version -var NextVersion = "v0.55.0" +var NextVersion = "v1.0.1" func (app *ElysApp) setUpgradeHandler() { app.UpgradeKeeper.SetUpgradeHandler( diff --git a/x/tradeshield/keeper/msg_server_execute_order_test.go b/x/tradeshield/keeper/msg_server_execute_order_test.go index 973084a97..957e168b9 100644 --- a/x/tradeshield/keeper/msg_server_execute_order_test.go +++ b/x/tradeshield/keeper/msg_server_execute_order_test.go @@ -4,6 +4,7 @@ import ( "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" oracletypes "github.com/elys-network/elys/x/oracle/types" + ptypes "github.com/elys-network/elys/x/parameter/types" keeper "github.com/elys-network/elys/x/tradeshield/keeper" "github.com/elys-network/elys/x/tradeshield/types" ) @@ -12,9 +13,10 @@ func (suite *TradeshieldKeeperTestSuite) TestMsgServerExecuteOrder() { addr := suite.AddAccounts(3, nil) testCases := []struct { - name string - expectErrMsg string - prerequisiteFunction func() *types.MsgExecuteOrders + name string + expectErrMsg string + prerequisiteFunction func() *types.MsgExecuteOrders + postrequisiteFunction func() }{ { "Spot Order not found", @@ -26,6 +28,7 @@ func (suite *TradeshieldKeeperTestSuite) TestMsgServerExecuteOrder() { PerpetualOrderIds: []uint64{1}, } }, + func() {}, }, { "Perpetual order not found", @@ -52,9 +55,77 @@ func (suite *TradeshieldKeeperTestSuite) TestMsgServerExecuteOrder() { PerpetualOrderIds: []uint64{1}, } }, + func() {}, }, { - "Success: Execute Orders", + "Success: Execute Spot Order", + "", + func() *types.MsgExecuteOrders { + suite.SetupCoinPrices() + + _ = suite.CreateNewAmmPool(addr[0], true, math.LegacyZeroDec(), math.LegacyZeroDec(), ptypes.ATOM, math.NewInt(100000000000).MulRaw(10), math.NewInt(100000000000).MulRaw(10)) + + openOrderMsg := &types.MsgCreateSpotOrder{ + OwnerAddress: addr[2].String(), + OrderType: types.SpotOrderType_LIMITBUY, + OrderPrice: types.OrderPrice{ + BaseDenom: "uusdc", + QuoteDenom: "uatom", + Rate: math.LegacyNewDec(10), + }, + OrderAmount: sdk.NewCoin("uusdc", math.NewInt(100000)), + OrderTargetDenom: "uatom", + } + msgSrvr := keeper.NewMsgServerImpl(suite.app.TradeshieldKeeper) + _, err := msgSrvr.CreateSpotOrder(suite.ctx, openOrderMsg) + suite.Require().NoError(err) + + suite.app.OracleKeeper.SetPrice(suite.ctx, oracletypes.Price{ + Asset: "ATOM", + Price: math.LegacyNewDec(5), + Source: "elys", + Provider: oracleProvider.String(), + Timestamp: uint64(suite.ctx.BlockTime().Unix()), + }) + + return &types.MsgExecuteOrders{ + Creator: addr[2].String(), + SpotOrderIds: []uint64{1}, + PerpetualOrderIds: []uint64{}, + } + }, + func() { + // Get events from context + events := suite.ctx.EventManager().Events() + + // Find the specific event we're looking for + var foundEvent sdk.Event + for _, event := range events { + if event.Type == types.TypeEvtExecuteLimitBuySpotOrder { + foundEvent = event + break + } + } + + // Assert event was emitted + suite.Require().NotNil(foundEvent) + + // Check event attributes + suite.Require().Equal(types.TypeEvtExecuteLimitBuySpotOrder, foundEvent.Type) + + // Check specific attributes + for _, attr := range foundEvent.Attributes { + switch string(attr.Key) { + case "order_id": + suite.Require().Equal("1", string(attr.Value)) + case "order_price": + suite.Require().Equal(string(attr.Value), "{\"base_denom\":\"uusdc\",\"quote_denom\":\"uatom\",\"rate\":\"5.000000000000000000\"}") + } + } + }, + }, + { + "Success: Execute Perpetual Order", "", func() *types.MsgExecuteOrders { _, _, _ = suite.SetPerpetualPool(1) @@ -86,10 +157,39 @@ func (suite *TradeshieldKeeperTestSuite) TestMsgServerExecuteOrder() { return &types.MsgExecuteOrders{ Creator: addr[2].String(), - SpotOrderIds: []uint64{1}, + SpotOrderIds: []uint64{}, PerpetualOrderIds: []uint64{1}, } }, + func() { + // Get events from context + events := suite.ctx.EventManager().Events() + + // Find the specific event we're looking for + var foundEvent sdk.Event + for _, event := range events { + if event.Type == types.TypeEvtExecuteLimitOpenPerpetualOrder { + foundEvent = event + break + } + } + + // Assert event was emitted + suite.Require().NotNil(foundEvent) + + // Check event attributes + suite.Require().Equal(types.TypeEvtExecuteLimitOpenPerpetualOrder, foundEvent.Type) + + // Check specific attributes + for _, attr := range foundEvent.Attributes { + switch string(attr.Key) { + case "order_id": + suite.Require().Equal("1", string(attr.Value)) + case "trigger_price": + suite.Require().Equal(string(attr.Value), "{\"trading_asset_denom\":\"uatom\",\"rate\":\"10.000000000000000000\"}") + } + } + }, }, } @@ -104,6 +204,7 @@ func (suite *TradeshieldKeeperTestSuite) TestMsgServerExecuteOrder() { } else { suite.Require().NoError(err) } + tc.postrequisiteFunction() }) } } diff --git a/x/tradeshield/keeper/msg_server_execute_orders.go b/x/tradeshield/keeper/msg_server_execute_orders.go index 3ace36885..2df525fbb 100644 --- a/x/tradeshield/keeper/msg_server_execute_orders.go +++ b/x/tradeshield/keeper/msg_server_execute_orders.go @@ -6,7 +6,6 @@ import ( "strings" sdk "github.com/cosmos/cosmos-sdk/types" - ammtypes "github.com/elys-network/elys/x/amm/types" "github.com/elys-network/elys/x/tradeshield/types" ) @@ -23,30 +22,27 @@ func (k msgServer) ExecuteOrders(goCtx context.Context, msg *types.MsgExecuteOrd } var err error - var res *ammtypes.MsgSwapByDenomResponse // dispatch based on the order type switch spotOrder.OrderType { case types.SpotOrderType_STOPLOSS: // execute the stop loss order - res, err = k.ExecuteStopLossOrder(ctx, spotOrder) + _, err = k.ExecuteStopLossOrder(ctx, spotOrder) case types.SpotOrderType_LIMITSELL: // execute the limit sell order - res, err = k.ExecuteLimitSellOrder(ctx, spotOrder) + _, err = k.ExecuteLimitSellOrder(ctx, spotOrder) case types.SpotOrderType_LIMITBUY: // execute the limit buy order - res, err = k.ExecuteLimitBuyOrder(ctx, spotOrder) + _, err = k.ExecuteLimitBuyOrder(ctx, spotOrder) case types.SpotOrderType_MARKETBUY: // execute the market buy order - res, err = k.ExecuteMarketBuyOrder(ctx, spotOrder) + _, err = k.ExecuteMarketBuyOrder(ctx, spotOrder) } // log the error if any if err != nil { // Add log about error or not executed spotLog = append(spotLog, fmt.Sprintf("Spot order Id:%d cannot be executed due to err: %s", spotOrderId, err.Error())) - } else { - ctx.EventManager().EmitEvent(types.NewExecuteSpotOrderEvt(spotOrder, res)) } } diff --git a/x/tradeshield/keeper/msg_server_spot_order.go b/x/tradeshield/keeper/msg_server_spot_order.go index 9b1f6277d..5917d7979 100644 --- a/x/tradeshield/keeper/msg_server_spot_order.go +++ b/x/tradeshield/keeper/msg_server_spot_order.go @@ -30,7 +30,7 @@ func (k msgServer) CreateSpotOrder(goCtx context.Context, msg *types.MsgCreateSp return nil, err } - ctx.EventManager().EmitEvent(types.NewExecuteSpotOrderEvt(pendingSpotOrder, res)) + ctx.EventManager().EmitEvent(types.NewExecuteMarketBuySpotOrderEvt(pendingSpotOrder, res)) return &types.MsgCreateSpotOrderResponse{ OrderId: pendingSpotOrder.OrderId, diff --git a/x/tradeshield/keeper/pending_spot_order.go b/x/tradeshield/keeper/pending_spot_order.go index 7891f89f4..d56cc09bf 100644 --- a/x/tradeshield/keeper/pending_spot_order.go +++ b/x/tradeshield/keeper/pending_spot_order.go @@ -212,6 +212,7 @@ func (k Keeper) ExecuteStopLossOrder(ctx sdk.Context, order types.SpotOrder) (*a DenomIn: order.OrderPrice.BaseDenom, DenomOut: order.OrderPrice.QuoteDenom, MinAmount: sdk.NewCoin(order.OrderTargetDenom, sdkmath.ZeroInt()), + MaxAmount: order.OrderAmount, }) if err != nil { return res, err @@ -220,6 +221,9 @@ func (k Keeper) ExecuteStopLossOrder(ctx sdk.Context, order types.SpotOrder) (*a // Remove the order from the pending order list k.RemovePendingSpotOrder(ctx, order.OrderId) + // emit the event + ctx.EventManager().EmitEvent(types.NewExecuteStopLossSpotOrderEvt(order, res)) + return res, nil } @@ -253,6 +257,7 @@ func (k Keeper) ExecuteLimitSellOrder(ctx sdk.Context, order types.SpotOrder) (* DenomIn: order.OrderPrice.BaseDenom, DenomOut: order.OrderPrice.QuoteDenom, MinAmount: sdk.NewCoin(order.OrderTargetDenom, sdkmath.ZeroInt()), + MaxAmount: order.OrderAmount, }) if err != nil { return res, err @@ -261,6 +266,9 @@ func (k Keeper) ExecuteLimitSellOrder(ctx sdk.Context, order types.SpotOrder) (* // Remove the order from the pending order list k.RemovePendingSpotOrder(ctx, order.OrderId) + // emit the event + ctx.EventManager().EmitEvent(types.NewExecuteLimitSellSpotOrderEvt(order, res)) + return res, nil } @@ -294,6 +302,7 @@ func (k Keeper) ExecuteLimitBuyOrder(ctx sdk.Context, order types.SpotOrder) (*a DenomIn: order.OrderPrice.BaseDenom, DenomOut: order.OrderPrice.QuoteDenom, MinAmount: sdk.NewCoin(order.OrderTargetDenom, sdkmath.ZeroInt()), + MaxAmount: order.OrderAmount, }) if err != nil { return res, err @@ -302,6 +311,9 @@ func (k Keeper) ExecuteLimitBuyOrder(ctx sdk.Context, order types.SpotOrder) (*a // Remove the order from the pending order list k.RemovePendingSpotOrder(ctx, order.OrderId) + // emit the event + ctx.EventManager().EmitEvent(types.NewExecuteLimitBuySpotOrderEvt(order, res)) + return res, nil } @@ -315,10 +327,14 @@ func (k Keeper) ExecuteMarketBuyOrder(ctx sdk.Context, order types.SpotOrder) (* DenomIn: order.OrderAmount.Denom, DenomOut: order.OrderTargetDenom, MinAmount: sdk.NewCoin(order.OrderTargetDenom, sdkmath.ZeroInt()), + MaxAmount: order.OrderAmount, }) if err != nil { return res, err } + // emit the event + ctx.EventManager().EmitEvent(types.NewExecuteMarketBuySpotOrderEvt(order, res)) + return res, nil } diff --git a/x/tradeshield/types/events.go b/x/tradeshield/types/events.go index 237abd472..2c4308e6d 100644 --- a/x/tradeshield/types/events.go +++ b/x/tradeshield/types/events.go @@ -1,6 +1,7 @@ package types import ( + "encoding/json" "strconv" sdk "github.com/cosmos/cosmos-sdk/types" @@ -12,7 +13,10 @@ const ( TypeEvtCancelPerpetualOrder = "tradeshield/cancel_perpetual_order" TypeEvtExecuteOrders = "tradeshield/execute_orders" TypeEvtExecuteLimitOpenPerpetualOrder = "tradeshield/execute_perpetual_limit_open_order" - TypeEvtExecuteSpotOrder = "tradeshield/execute_spot_order" + TypeEvtExecuteLimitBuySpotOrder = "tradeshield/execute_limit_buy_spot_order" + TypeEvtExecuteLimitSellSpotOrder = "tradeshield/execute_limit_sell_spot_order" + TypeEvtExecuteStopLossSpotOrder = "tradeshield/execute_stop_loss_spot_order" + TypeEvtExecuteMarketBuySpotOrder = "tradeshield/execute_market_buy_spot_order" ) func EmitCloseSpotOrderEvent(ctx sdk.Context, order SpotOrder) { @@ -57,11 +61,86 @@ func NewCancelPerpetualOrderEvt(order PerpetualOrder) sdk.Event { ) } -func NewExecuteSpotOrderEvt(order SpotOrder, res *ammtypes.MsgSwapByDenomResponse) sdk.Event { - return sdk.NewEvent(TypeEvtExecuteSpotOrder, +func NewExecuteLimitBuySpotOrderEvt(order SpotOrder, res *ammtypes.MsgSwapByDenomResponse) sdk.Event { + // convert order price to json string + orderPrice, err := json.Marshal(order.OrderPrice) + if err != nil { + panic(err) + } + + return sdk.NewEvent(TypeEvtExecuteLimitBuySpotOrder, sdk.NewAttribute("order_type", order.OrderType.String()), sdk.NewAttribute("order_id", strconv.FormatInt(int64(order.OrderId), 10)), - sdk.NewAttribute("order_price", order.OrderPrice.String()), + sdk.NewAttribute("order_price", string(orderPrice)), + sdk.NewAttribute("order_amount", order.OrderAmount.String()), + sdk.NewAttribute("owner_address", order.OwnerAddress), + sdk.NewAttribute("order_target_denom", order.OrderTargetDenom), + sdk.NewAttribute("date", order.Date.String()), + sdk.NewAttribute("amount", res.Amount.String()), + sdk.NewAttribute("spot_price", res.SpotPrice.String()), + sdk.NewAttribute("swap_fee", res.SwapFee.String()), + sdk.NewAttribute("discount", res.Discount.String()), + sdk.NewAttribute("recipient", res.Recipient), + ) +} + +func NewExecuteLimitSellSpotOrderEvt(order SpotOrder, res *ammtypes.MsgSwapByDenomResponse) sdk.Event { + // convert order price to json string + orderPrice, err := json.Marshal(order.OrderPrice) + if err != nil { + panic(err) + } + + return sdk.NewEvent(TypeEvtExecuteLimitSellSpotOrder, + sdk.NewAttribute("order_type", order.OrderType.String()), + sdk.NewAttribute("order_id", strconv.FormatInt(int64(order.OrderId), 10)), + sdk.NewAttribute("order_price", string(orderPrice)), + sdk.NewAttribute("order_amount", order.OrderAmount.String()), + sdk.NewAttribute("owner_address", order.OwnerAddress), + sdk.NewAttribute("order_target_denom", order.OrderTargetDenom), + sdk.NewAttribute("date", order.Date.String()), + sdk.NewAttribute("amount", res.Amount.String()), + sdk.NewAttribute("spot_price", res.SpotPrice.String()), + sdk.NewAttribute("swap_fee", res.SwapFee.String()), + sdk.NewAttribute("discount", res.Discount.String()), + sdk.NewAttribute("recipient", res.Recipient), + ) +} + +func NewExecuteStopLossSpotOrderEvt(order SpotOrder, res *ammtypes.MsgSwapByDenomResponse) sdk.Event { + // convert order price to json string + orderPrice, err := json.Marshal(order.OrderPrice) + if err != nil { + panic(err) + } + + return sdk.NewEvent(TypeEvtExecuteStopLossSpotOrder, + sdk.NewAttribute("order_type", order.OrderType.String()), + sdk.NewAttribute("order_id", strconv.FormatInt(int64(order.OrderId), 10)), + sdk.NewAttribute("order_price", string(orderPrice)), + sdk.NewAttribute("order_amount", order.OrderAmount.String()), + sdk.NewAttribute("owner_address", order.OwnerAddress), + sdk.NewAttribute("order_target_denom", order.OrderTargetDenom), + sdk.NewAttribute("date", order.Date.String()), + sdk.NewAttribute("amount", res.Amount.String()), + sdk.NewAttribute("spot_price", res.SpotPrice.String()), + sdk.NewAttribute("swap_fee", res.SwapFee.String()), + sdk.NewAttribute("discount", res.Discount.String()), + sdk.NewAttribute("recipient", res.Recipient), + ) +} + +func NewExecuteMarketBuySpotOrderEvt(order SpotOrder, res *ammtypes.MsgSwapByDenomResponse) sdk.Event { + // convert order price to json string + orderPrice, err := json.Marshal(order.OrderPrice) + if err != nil { + panic(err) + } + + return sdk.NewEvent(TypeEvtExecuteMarketBuySpotOrder, + sdk.NewAttribute("order_type", order.OrderType.String()), + sdk.NewAttribute("order_id", strconv.FormatInt(int64(order.OrderId), 10)), + sdk.NewAttribute("order_price", string(orderPrice)), sdk.NewAttribute("order_amount", order.OrderAmount.String()), sdk.NewAttribute("owner_address", order.OwnerAddress), sdk.NewAttribute("order_target_denom", order.OrderTargetDenom), @@ -75,12 +154,18 @@ func NewExecuteSpotOrderEvt(order SpotOrder, res *ammtypes.MsgSwapByDenomRespons } func NewExecuteLimitOpenPerpetualOrderEvt(order PerpetualOrder, positionId uint64) sdk.Event { + // convert trigger price to json string + triggerPrice, err := json.Marshal(order.TriggerPrice) + if err != nil { + panic(err) + } + return sdk.NewEvent(TypeEvtExecuteLimitOpenPerpetualOrder, sdk.NewAttribute("order_id", strconv.FormatInt(int64(order.OrderId), 10)), sdk.NewAttribute("owner_address", order.OwnerAddress), sdk.NewAttribute("order_type", order.PerpetualOrderType.String()), sdk.NewAttribute("position", order.Position.String()), sdk.NewAttribute("position_id", strconv.FormatInt(int64(positionId), 10)), - sdk.NewAttribute("trigger_price", order.TriggerPrice.String()), + sdk.NewAttribute("trigger_price", string(triggerPrice)), ) }