Skip to content

Commit

Permalink
[1659]: Set the partial order and emit events during settlement.
Browse files Browse the repository at this point in the history
  • Loading branch information
SpicyLemon committed Sep 23, 2023
1 parent 60d31da commit a9f219d
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 22 deletions.
2 changes: 1 addition & 1 deletion x/exchange/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func NewEventOrderFilled(orderID uint64) *EventOrderFilled {
}
}

func NewEventOrderPartiallyFilled(orderID uint64, assetsFilled, feesFilled sdk.Coins) *EventOrderPartiallyFilled {
func NewEventOrderPartiallyFilled(orderID uint64, assetsFilled, feesFilled sdk.Coin) *EventOrderPartiallyFilled {
return &EventOrderPartiallyFilled{
OrderId: orderID,
AssetsFilled: assetsFilled.String(),
Expand Down
16 changes: 9 additions & 7 deletions x/exchange/events_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ func TestNewEventOrderFilled(t *testing.T) {

func TestNewEventOrderPartiallyFilled(t *testing.T) {
orderID := uint64(18)
assetsFilled := sdk.NewCoins(sdk.NewInt64Coin("first", 111), sdk.NewInt64Coin("second", 22))
feesFilled := sdk.NewCoins(sdk.NewInt64Coin("charge", 8), sdk.NewInt64Coin("fee", 15))
assetsFilled := sdk.NewInt64Coin("acoin", 111)
feesFilled := sdk.NewInt64Coin("fcoin", 8)

event := NewEventOrderPartiallyFilled(orderID, assetsFilled, feesFilled)
assert.Equal(t, orderID, event.OrderId, "OrderId")
Expand Down Expand Up @@ -299,8 +299,10 @@ func TestTypedEventToEvent(t *testing.T) {
updatedByQ := quoteBz(updatedBy.String())
coins1 := sdk.NewCoins(sdk.NewInt64Coin("onecoin", 1), sdk.NewInt64Coin("twocoin", 2))
coins1Q := quoteBz(coins1.String())
coins2 := sdk.NewCoins(sdk.NewInt64Coin("threecoin", 3), sdk.NewInt64Coin("fourcoin", 4))
coins2Q := quoteBz(coins2.String())
acoin := sdk.NewInt64Coin("acoin", 5)
acoinQ := quoteBz(acoin.String())
fcoin := sdk.NewInt64Coin("fcoin", 5)
fcoinQ := quoteBz(fcoin.String())

tests := []struct {
name string
Expand Down Expand Up @@ -352,12 +354,12 @@ func TestTypedEventToEvent(t *testing.T) {
},
{
name: "EventOrderPartiallyFilled",
tev: NewEventOrderPartiallyFilled(5, coins1, coins2),
tev: NewEventOrderPartiallyFilled(5, acoin, fcoin),
expEvent: sdk.Event{
Type: "provenance.exchange.v1.EventOrderPartiallyFilled",
Attributes: []abci.EventAttribute{
{Key: []byte("assets_filled"), Value: coins1Q},
{Key: []byte("fees_filled"), Value: coins2Q},
{Key: []byte("assets_filled"), Value: acoinQ},
{Key: []byte("fees_filled"), Value: fcoinQ},
{Key: []byte("order_id"), Value: quoteBz("5")},
},
},
Expand Down
81 changes: 73 additions & 8 deletions x/exchange/fulfillment.go
Original file line number Diff line number Diff line change
Expand Up @@ -505,8 +505,63 @@ func getFulfillmentAssetsAmt(askOF, bidOF *OrderFulfillment) (sdkmath.Int, error
return bidAmtLeft, nil

Check warning on line 505 in x/exchange/fulfillment.go

View check run for this annotation

Codecov / codecov/patch

x/exchange/fulfillment.go#L502-L505

Added lines #L502 - L505 were not covered by tests
}

type PartialFulfillment struct {
NewOrder *Order
AssetsFilled sdk.Coin
PriceFilled sdk.Coin
}

func NewPartialFulfillment(f *OrderFulfillment) *PartialFulfillment {
order := NewOrder(f.GetOrderID())
if f.IsAskOrder() {
askOrder := &AskOrder{
MarketId: f.GetMarketID(),
Seller: f.GetOwner(),
Assets: f.GetAssetsLeft(),
Price: f.GetPriceLeft(),
AllowPartial: f.PartialFillAllowed(),
}
if !f.FeesLeft.IsZero() {
if len(f.FeesLeft) > 1 {
panic(fmt.Errorf("partially filled ask order %d somehow has multiple denoms in fees left %q",
order.OrderId, f.FeesLeft))

Check warning on line 527 in x/exchange/fulfillment.go

View check run for this annotation

Codecov / codecov/patch

x/exchange/fulfillment.go#L514-L527

Added lines #L514 - L527 were not covered by tests
}
askOrder.SellerSettlementFlatFee = &f.FeesLeft[0]

Check warning on line 529 in x/exchange/fulfillment.go

View check run for this annotation

Codecov / codecov/patch

x/exchange/fulfillment.go#L529

Added line #L529 was not covered by tests
}
return &PartialFulfillment{
NewOrder: order,
AssetsFilled: f.GetAssetsFilled(),
PriceFilled: f.GetPriceFilled(),
}

Check warning on line 535 in x/exchange/fulfillment.go

View check run for this annotation

Codecov / codecov/patch

x/exchange/fulfillment.go#L531-L535

Added lines #L531 - L535 were not covered by tests
}

if f.IsBidOrder() {
bidOrder := &BidOrder{
MarketId: f.GetMarketID(),
Buyer: f.GetOwner(),
Assets: f.GetAssetsLeft(),
Price: f.GetPriceLeft(),
BuyerSettlementFees: f.FeesLeft,
AllowPartial: f.PartialFillAllowed(),
}
return &PartialFulfillment{
NewOrder: order.WithBid(bidOrder),
AssetsFilled: f.GetAssetsFilled(),
PriceFilled: f.GetPriceFilled(),
}
}

Check warning on line 552 in x/exchange/fulfillment.go

View check run for this annotation

Codecov / codecov/patch

x/exchange/fulfillment.go#L538-L552

Added lines #L538 - L552 were not covered by tests

panic(fmt.Errorf("order %d has unknown type %q", order.OrderId, f.GetOrderType()))

Check warning on line 554 in x/exchange/fulfillment.go

View check run for this annotation

Codecov / codecov/patch

x/exchange/fulfillment.go#L554

Added line #L554 was not covered by tests
}

type Fulfillments struct {
AskOFs []*OrderFulfillment
BidOFs []*OrderFulfillment
PartialOrder *PartialFulfillment
}

// BuildFulfillments creates all of the ask and bid order fulfillments.
func BuildFulfillments(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) ([]*OrderFulfillment, []*OrderFulfillment, error) {
func BuildFulfillments(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio) (*Fulfillments, error) {
askOFs := make([]*OrderFulfillment, len(askOrders))
for i, askOrder := range askOrders {
askOFs[i] = NewOrderFulfillment(askOrder)
Expand All @@ -520,12 +575,12 @@ func BuildFulfillments(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio)
for a < len(askOFs) && b < len(bidOFs) {
err := Fulfill(askOFs[a], bidOFs[b])
if err != nil {
return nil, nil, err
return nil, err
}
askFilled := askOFs[a].IsFullyFilled()
bidFilled := bidOFs[b].IsFullyFilled()
if !askFilled && !bidFilled {
return nil, nil, fmt.Errorf("neither ask order %d nor bid order %d could be filled in full",
return nil, fmt.Errorf("neither ask order %d nor bid order %d could be filled in full",
askOFs[a].GetOrderID(), bidOFs[b].GetOrderID())
}
if askFilled {
Expand All @@ -539,27 +594,37 @@ func BuildFulfillments(askOrders, bidOrders []*Order, sellerFeeRatio *FeeRatio)
// Need to finalize bid orders first due to possible extra price distribution.
for _, bidOF := range bidOFs {
if err := bidOF.Finalize(sellerFeeRatio); err != nil {
return nil, nil, err
return nil, err
}

Check warning on line 598 in x/exchange/fulfillment.go

View check run for this annotation

Codecov / codecov/patch

x/exchange/fulfillment.go#L595-L598

Added lines #L595 - L598 were not covered by tests
}
for _, askOF := range askOFs {
if err := askOF.Finalize(sellerFeeRatio); err != nil {
return nil, nil, err
return nil, err
}

Check warning on line 603 in x/exchange/fulfillment.go

View check run for this annotation

Codecov / codecov/patch

x/exchange/fulfillment.go#L600-L603

Added lines #L600 - L603 were not covered by tests
}

for _, bidOF := range bidOFs {
if err := bidOF.Validate(); err != nil {
return nil, nil, err
return nil, err
}

Check warning on line 609 in x/exchange/fulfillment.go

View check run for this annotation

Codecov / codecov/patch

x/exchange/fulfillment.go#L606-L609

Added lines #L606 - L609 were not covered by tests
}
for _, askOF := range askOFs {
if err := askOF.Validate(); err != nil {
return nil, nil, err
return nil, err
}

Check warning on line 614 in x/exchange/fulfillment.go

View check run for this annotation

Codecov / codecov/patch

x/exchange/fulfillment.go#L611-L614

Added lines #L611 - L614 were not covered by tests
}

return askOFs, bidOFs, nil
rv := &Fulfillments{
AskOFs: askOFs,
BidOFs: bidOFs,
}
if !askOFs[len(askOFs)-1].IsFullyFilled() {
rv.PartialOrder = NewPartialFulfillment(askOFs[len(askOFs)-1])
} else if !bidOFs[len(bidOFs)-1].IsFullyFilled() {
rv.PartialOrder = NewPartialFulfillment(bidOFs[len(bidOFs)-1])
}

Check warning on line 625 in x/exchange/fulfillment.go

View check run for this annotation

Codecov / codecov/patch

x/exchange/fulfillment.go#L617-L625

Added lines #L617 - L625 were not covered by tests

return rv, nil

Check warning on line 627 in x/exchange/fulfillment.go

View check run for this annotation

Codecov / codecov/patch

x/exchange/fulfillment.go#L627

Added line #L627 was not covered by tests
}

// Transfer contains bank inputs and outputs indicating a transfer that needs to be made.
Expand Down
38 changes: 32 additions & 6 deletions x/exchange/keeper/fulfillment.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,11 +329,12 @@ func (k Keeper) SettleOrders(ctx sdk.Context, marketID uint32, askOrderIDs, bidO
return errors.Join(errs...)
}

Check warning on line 330 in x/exchange/keeper/fulfillment.go

View check run for this annotation

Codecov / codecov/patch

x/exchange/keeper/fulfillment.go#L320-L330

Added lines #L320 - L330 were not covered by tests

if !expectPartial && !exchange.CoinsEquals(totalAssetsForSale, totalAssetsToBuy) {
isPartial := !exchange.CoinsEquals(totalAssetsForSale, totalAssetsToBuy)
if !expectPartial && isPartial {
return fmt.Errorf("total assets for sale %q does not equal total assets to buy %q and partial settlement not expected",
totalAssetsForSale, totalAssetsToBuy)
}
if expectPartial && exchange.CoinsEquals(totalAssetsForSale, totalAssetsToBuy) {
if expectPartial && !isPartial {
return fmt.Errorf("total assets for sale %q equals total assets to buy %q but partial settlement is expected",
totalAssetsForSale, totalAssetsToBuy)
}

Check warning on line 340 in x/exchange/keeper/fulfillment.go

View check run for this annotation

Codecov / codecov/patch

x/exchange/keeper/fulfillment.go#L332-L340

Added lines #L332 - L340 were not covered by tests
Expand All @@ -343,12 +344,12 @@ func (k Keeper) SettleOrders(ctx sdk.Context, marketID uint32, askOrderIDs, bidO
return err
}

Check warning on line 345 in x/exchange/keeper/fulfillment.go

View check run for this annotation

Codecov / codecov/patch

x/exchange/keeper/fulfillment.go#L342-L345

Added lines #L342 - L345 were not covered by tests

askOFs, bidOfs, err := exchange.BuildFulfillments(askOrders, bidOrders, sellerFeeRatio)
fulfillments, err := exchange.BuildFulfillments(askOrders, bidOrders, sellerFeeRatio)
if err != nil {
return err
}

Check warning on line 350 in x/exchange/keeper/fulfillment.go

View check run for this annotation

Codecov / codecov/patch

x/exchange/keeper/fulfillment.go#L347-L350

Added lines #L347 - L350 were not covered by tests

transfers := exchange.BuildSettlementTransfers(askOFs, bidOfs)
transfers := exchange.BuildSettlementTransfers(fulfillments.AskOFs, fulfillments.BidOFs)

for _, transfer := range transfers.OrderTransfers {
if err = k.DoTransfer(ctx, transfer.Inputs, transfer.Outputs); err != nil {
Expand All @@ -360,6 +361,31 @@ func (k Keeper) SettleOrders(ctx sdk.Context, marketID uint32, askOrderIDs, bidO
return err
}

Check warning on line 362 in x/exchange/keeper/fulfillment.go

View check run for this annotation

Codecov / codecov/patch

x/exchange/keeper/fulfillment.go#L360-L362

Added lines #L360 - L362 were not covered by tests

// TODO[1658]: Update the partial order and emit events.
panic("not finished")
if fulfillments.PartialOrder != nil {
if err = k.setOrderInStore(store, *fulfillments.PartialOrder.NewOrder); err != nil {
return fmt.Errorf("could not update partial %s order %d: %w",
fulfillments.PartialOrder.NewOrder.GetOrderType(), fulfillments.PartialOrder.NewOrder.OrderId, err)
}

Check warning on line 368 in x/exchange/keeper/fulfillment.go

View check run for this annotation

Codecov / codecov/patch

x/exchange/keeper/fulfillment.go#L364-L368

Added lines #L364 - L368 were not covered by tests
}

events := make([]proto.Message, 0, len(askOrders)+len(bidOrders))
for _, order := range askOrders {
if fulfillments.PartialOrder != nil && fulfillments.PartialOrder.NewOrder.OrderId != order.OrderId {
events = append(events, exchange.NewEventOrderFilled(order.OrderId))
}

Check warning on line 375 in x/exchange/keeper/fulfillment.go

View check run for this annotation

Codecov / codecov/patch

x/exchange/keeper/fulfillment.go#L371-L375

Added lines #L371 - L375 were not covered by tests
}
for _, order := range bidOrders {
if fulfillments.PartialOrder != nil && fulfillments.PartialOrder.NewOrder.OrderId != order.OrderId {
events = append(events, exchange.NewEventOrderFilled(order.OrderId))
}

Check warning on line 380 in x/exchange/keeper/fulfillment.go

View check run for this annotation

Codecov / codecov/patch

x/exchange/keeper/fulfillment.go#L377-L380

Added lines #L377 - L380 were not covered by tests
}
if fulfillments.PartialOrder != nil {
events = append(events, exchange.NewEventOrderPartiallyFilled(
fulfillments.PartialOrder.NewOrder.OrderId,
fulfillments.PartialOrder.AssetsFilled,
fulfillments.PartialOrder.PriceFilled,
))
}

Check warning on line 388 in x/exchange/keeper/fulfillment.go

View check run for this annotation

Codecov / codecov/patch

x/exchange/keeper/fulfillment.go#L382-L388

Added lines #L382 - L388 were not covered by tests

return ctx.EventManager().EmitTypedEvents(events...)

Check warning on line 390 in x/exchange/keeper/fulfillment.go

View check run for this annotation

Codecov / codecov/patch

x/exchange/keeper/fulfillment.go#L390

Added line #L390 was not covered by tests
}

0 comments on commit a9f219d

Please sign in to comment.