diff --git a/x/exchange/keeper/invariants.go b/x/exchange/keeper/invariants.go index 0b126e57..268e04f7 100644 --- a/x/exchange/keeper/invariants.go +++ b/x/exchange/keeper/invariants.go @@ -75,6 +75,11 @@ func OrderStateInvariant(k Keeper) sdk.Invariant { msg += fmt.Sprintf("\torder %d should have been deleted since it has no executable quantity\n", order.Id) cnt++ } + if !order.RemainingDeposit.TruncateDec().Equal(order.RemainingDeposit) { + msg += fmt.Sprintf("\torder %d should have integer remaining deposit but has %s\n", + order.Id, order.RemainingDeposit) + cnt++ + } return false }) broken := cnt != 0 diff --git a/x/exchange/keeper/matching.go b/x/exchange/keeper/matching.go index 01b7009e..6482a80f 100644 --- a/x/exchange/keeper/matching.go +++ b/x/exchange/keeper/matching.go @@ -94,11 +94,12 @@ func (k Keeper) finalizeMatching(ctx sdk.Context, market types.Market, orders [] ordererAddr := memOrder.OrdererAddress() if memOrder.IsMatched() { receivedCoin := sdk.NewDecCoinFromDec(receiveDenom, memOrder.Received()) - escrow.Unlock(ordererAddr, receivedCoin) if memOrder.Type() == types.UserMemOrder { + paid := memOrder.Paid().Ceil() + receivedCoin.Amount = receivedCoin.Amount.TruncateDec() order := memOrder.Order() order.OpenQuantity = order.OpenQuantity.Sub(memOrder.ExecutedQuantity()) - order.RemainingDeposit = memOrder.RemainingDeposit() + order.RemainingDeposit = order.RemainingDeposit.Sub(paid) if err := ctx.EventManager().EmitTypedEvent(&types.EventOrderFilled{ MarketId: market.Id, OrderId: order.Id, @@ -108,7 +109,7 @@ func (k Keeper) finalizeMatching(ctx sdk.Context, market types.Market, orders [] Quantity: order.Quantity, OpenQuantity: order.OpenQuantity, ExecutedQuantity: memOrder.ExecutedQuantity(), - Paid: sdk.NewDecCoinFromDec(payDenom, memOrder.Paid()), + Paid: sdk.NewDecCoinFromDec(payDenom, paid), Received: receivedCoin, }); err != nil { return err @@ -124,6 +125,7 @@ func (k Keeper) finalizeMatching(ctx sdk.Context, market types.Market, orders [] k.SetOrder(ctx, order) } } + escrow.Unlock(ordererAddr, receivedCoin) } // Should refund deposit if memOrder.Type() == types.OrderSourceMemOrder && memOrder.RemainingDeposit().IsPositive() { @@ -171,7 +173,8 @@ func (k Keeper) finalizeMatching(ctx sdk.Context, market types.Market, orders [] if totalFee.IsNegative() { paid.Amount = paid.Amount.Add(totalFee) } - received := sdk.NewDecCoinFromDec(receiveDenom, totalReceived) + paid.Amount = paid.Amount.Ceil() + received := sdk.NewDecCoinFromDec(receiveDenom, totalReceived.TruncateDec()) if err := ctx.EventManager().EmitTypedEvent(&types.EventOrderSourceOrdersFilled{ MarketId: market.Id, SourceName: sourceName, diff --git a/x/exchange/keeper/order_test.go b/x/exchange/keeper/order_test.go index 8a37d59a..91d02733 100644 --- a/x/exchange/keeper/order_test.go +++ b/x/exchange/keeper/order_test.go @@ -306,8 +306,11 @@ func (s *KeeperTestSuite) TestDecQuantity() { s.PlaceMarketOrder(market.Id, ordererAddr3, false, sdk.NewDec(10000)) orderer3BalancesAfter := s.GetAllBalances(ordererAddr3) + // order2 remaining deposit = 380uusd + // order2 executable quantity ~= 2699.6305768 + // order2 executable quantity * 0.14076 ~= 379.9999999 diff, _ := orderer3BalancesAfter.SafeSub(orderer3BalancesBefore) - s.AssertEqual(sdk.NewInt(380), diff.AmountOf("uusd")) // 2699.7*0.14076=380.009722 + s.AssertEqual(sdk.NewInt(379), diff.AmountOf("uusd")) } func (s *KeeperTestSuite) TestNumMMOrdersEdgecase() { diff --git a/x/exchange/types/mem_order.go b/x/exchange/types/mem_order.go index b7d4f2c3..8bd6c9d9 100644 --- a/x/exchange/types/mem_order.go +++ b/x/exchange/types/mem_order.go @@ -159,7 +159,7 @@ func (order *MemOrder) ExecutableQuantity() sdk.Dec { if order.isBuy { return sdk.MinDec(executableQty, order.remainingDeposit.QuoTruncate(order.price)) } - return executableQty + return sdk.MinDec(executableQty, order.remainingDeposit) } func (order *MemOrder) HasPriorityOver(other *MemOrder) bool {