Skip to content

Commit

Permalink
Merge pull request #165 from crescent-network/fix-amm-module-1
Browse files Browse the repository at this point in the history
fix: fix bug in placeLimitOrder and exchange invariant checks
  • Loading branch information
kingcre authored Jul 5, 2023
2 parents 92ee8b8 + 21924a7 commit 05203ea
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 10 deletions.
21 changes: 21 additions & 0 deletions app/testutil/test_suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package testutil
import (
"time"

"github.com/gogo/protobuf/proto"
"github.com/stretchr/testify/suite"

sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -76,3 +77,23 @@ func (s *TestSuite) FundedAccount(addrNum int, amt sdk.Coins) sdk.AccAddress {
s.FundAccount(addr, amt)
return addr
}

func (s *TestSuite) CheckEvent(evtType proto.Message, attrs map[string][]byte) {
s.T().Helper()
evtTypeName := proto.MessageName(evtType)
for _, ev := range s.Ctx.EventManager().ABCIEvents() {
if ev.Type == evtTypeName {
attrMap := make(map[string][]byte)
for _, attr := range ev.Attributes {
attrMap[string(attr.Key)] = attr.Value
}
for k, v := range attrs {
value, ok := attrMap[k]
s.Require().Truef(ok, "key %s not found", k)
s.Require().Equal(v, value)
}
return
}
}
s.FailNowf("CheckEvent failed", "event with type %s not found", evtTypeName)
}
9 changes: 6 additions & 3 deletions x/exchange/keeper/invariants.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ func CanCancelOrderInvariant(k Keeper) sdk.Invariant {
msg := ""
cnt := 0
k.IterateAllOrders(ctx, func(order types.Order) (stop bool) {
if order.MsgHeight == ctx.BlockHeight() {
return false
}
if _, err := k.CancelOrder(ctx, order.MustGetOrdererAddress(), order.Id); err != nil {
msg += fmt.Sprintf("\tcannot cancel order %d: %v\n", order.Id, err)
cnt++
Expand Down Expand Up @@ -103,14 +106,14 @@ func OrderBookInvariant(k Keeper) sdk.Invariant {
}
}
marketState := k.MustGetMarketState(ctx, market.Id)
if marketState.LastPrice != nil {
if !bestSellPrice.IsNil() && bestSellPrice.LT(*marketState.LastPrice) {
if marketState.LastPrice != nil && !bestSellPrice.IsNil() && !bestBuyPrice.IsNil() {
if bestSellPrice.LT(*marketState.LastPrice) && bestBuyPrice.GTE(bestSellPrice) {
msg += fmt.Sprintf(
"\tmarket %d has sell order under the last price: %s < %s\n",
market.Id, bestSellPrice, marketState.LastPrice)
cnt++
}
if !bestBuyPrice.IsNil() && bestBuyPrice.GT(*marketState.LastPrice) {
if bestBuyPrice.GT(*marketState.LastPrice) && bestSellPrice.LTE(bestBuyPrice) {
msg += fmt.Sprintf(
"\tmarket %d has buy order above the last price: %s > %s\n",
market.Id, bestBuyPrice, marketState.LastPrice)
Expand Down
8 changes: 6 additions & 2 deletions x/exchange/keeper/order.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,15 @@ func (k Keeper) placeLimitOrder(
}
}

if isBatch || qty.Sub(res.ExecutedQuantity).IsPositive() {
openQty := qty
if !isBatch {
openQty = openQty.Sub(res.ExecutedQuantity)
}
if isBatch || openQty.IsPositive() {
deadline := ctx.BlockTime().Add(lifespan)
order, err = k.newOrder(
ctx, orderId, typ, market, ordererAddr, isBuy, price,
qty, qty.Sub(res.ExecutedQuantity), deadline, false)
qty, openQty, deadline, false)
if err != nil {
return
}
Expand Down
95 changes: 90 additions & 5 deletions x/exchange/keeper/order_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,99 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"

utils "github.com/crescent-network/crescent/v5/types"
"github.com/crescent-network/crescent/v5/x/exchange/keeper"
"github.com/crescent-network/crescent/v5/x/exchange/types"
)

func (s *KeeperTestSuite) TestOrderMatching() {
aliceAddr := utils.TestAddress(1)
bobAddr := utils.TestAddress(2)
func (s *KeeperTestSuite) TestPlaceLimitOrder() {
market := s.CreateMarket(utils.TestAddress(0), "ucre", "uusd", true)

ordererAddr1 := s.FundedAccount(1, enoughCoins)
ordererAddr2 := s.FundedAccount(2, enoughCoins)

msgServer := keeper.NewMsgServerImpl(s.keeper)
s.Ctx = s.Ctx.WithEventManager(sdk.NewEventManager())
resp, err := msgServer.PlaceLimitOrder(
sdk.WrapSDKContext(s.Ctx), types.NewMsgPlaceLimitOrder(
ordererAddr1, market.Id, true, utils.ParseDec("5.1"), sdk.NewInt(10_000000), time.Hour))
s.Require().NoError(err)
s.Require().EqualValues(1, resp.OrderId)
s.Require().Equal(sdk.NewInt(0), resp.ExecutedQuantity)
s.Require().Equal(sdk.NewInt64Coin("uusd", 0), resp.Paid)
s.Require().Equal(sdk.NewInt64Coin("ucre", 0), resp.Received)
s.CheckEvent(&types.EventPlaceLimitOrder{}, map[string][]byte{
"executed_quantity": []byte(`"0"`),
"paid": []byte(`{"denom":"uusd","amount":"0"}`),
"received": []byte(`{"denom":"ucre","amount":"0"}`),
})

s.Ctx = s.Ctx.WithEventManager(sdk.NewEventManager())
resp, err = msgServer.PlaceLimitOrder(
sdk.WrapSDKContext(s.Ctx), types.NewMsgPlaceLimitOrder(
ordererAddr2, market.Id, false, utils.ParseDec("5"), sdk.NewInt(5_000000), time.Hour))
s.Require().NoError(err)
s.Require().EqualValues(2, resp.OrderId)
s.Require().Equal(sdk.NewInt(5_000000), resp.ExecutedQuantity)
s.Require().Equal(sdk.NewInt64Coin("ucre", 5_000000), resp.Paid)
// Matched at 5.1
s.Require().Equal(sdk.NewInt64Coin("uusd", 25_423500), resp.Received)
s.CheckEvent(&types.EventPlaceLimitOrder{}, map[string][]byte{
"executed_quantity": []byte(`"5000000"`),
"paid": []byte(`{"denom":"ucre","amount":"5000000"}`),
"received": []byte(`{"denom":"uusd","amount":"25423500"}`),
})
}

func (s *KeeperTestSuite) TestPlaceBatchLimitOrder() {
market := s.CreateMarket(utils.TestAddress(0), "ucre", "uusd", true)

ordererAddr1 := s.FundedAccount(1, enoughCoins)
ordererAddr2 := s.FundedAccount(2, enoughCoins)

s.FundAccount(aliceAddr, utils.ParseCoins("1000000ucre,1000000uusd"))
s.FundAccount(bobAddr, utils.ParseCoins("1000000ucre,1000000uusd"))
ordererBalances1Before := s.GetAllBalances(ordererAddr1)
ordererBalances2Before := s.GetAllBalances(ordererAddr2)
s.PlaceBatchLimitOrder(
market.Id, ordererAddr1, true, utils.ParseDec("5.1"), sdk.NewInt(10_000000), 0)
s.PlaceBatchLimitOrder(
market.Id, ordererAddr2, false, utils.ParseDec("5"), sdk.NewInt(10_000000), 0)
s.Require().NoError(s.keeper.RunBatchMatching(s.Ctx, market))
s.NextBlock()

marketState := s.keeper.MustGetMarketState(s.Ctx, market.Id)
s.Require().NotNil(marketState.LastPrice)
s.Require().Equal("5.050000000000000000", marketState.LastPrice.String())
ordererBalances1After := s.GetAllBalances(ordererAddr1)
ordererBalances2After := s.GetAllBalances(ordererAddr2)
ordererBalances1Diff, _ := ordererBalances1After.SafeSub(ordererBalances1Before)
ordererBalances2Diff, _ := ordererBalances2After.SafeSub(ordererBalances2Before)
// Both taker
s.Require().Equal("9970000ucre,-50500000uusd", ordererBalances1Diff.String())
s.Require().Equal("-10000000ucre,50348500uusd", ordererBalances2Diff.String())

ordererBalances1Before = ordererBalances1After
ordererBalances2Before = ordererBalances2After
s.PlaceBatchLimitOrder(
market.Id, ordererAddr1, true, utils.ParseDec("5.2"), sdk.NewInt(10_000000), 0)
s.PlaceBatchLimitOrder(
market.Id, ordererAddr2, false, utils.ParseDec("5.07"), sdk.NewInt(10_000000), 0)
s.Require().NoError(s.keeper.RunBatchMatching(s.Ctx, market))
s.NextBlock()

marketState = s.keeper.MustGetMarketState(s.Ctx, market.Id)
s.Require().NotNil(marketState.LastPrice)
s.Require().Equal("5.070000000000000000", marketState.LastPrice.String())
ordererBalances1After = s.GetAllBalances(ordererAddr1)
ordererBalances2After = s.GetAllBalances(ordererAddr2)
ordererBalances1Diff, _ = ordererBalances1After.SafeSub(ordererBalances1Before)
ordererBalances2Diff, _ = ordererBalances2After.SafeSub(ordererBalances2Before)
s.Require().Equal("9970000ucre,-50700000uusd", ordererBalances1Diff.String())
// Maker
s.Require().Equal("-9985000ucre,50700000uusd", ordererBalances2Diff.String())
}

func (s *KeeperTestSuite) TestOrderMatching() {
aliceAddr := s.FundedAccount(1, utils.ParseCoins("1000000ucre,1000000uusd"))
bobAddr := s.FundedAccount(2, utils.ParseCoins("1000000ucre,1000000uusd"))

market := s.CreateMarket(utils.TestAddress(3), "ucre", "uusd", true)

Expand Down

0 comments on commit 05203ea

Please sign in to comment.