/
orderbook_settle.go
200 lines (172 loc) · 6.07 KB
/
orderbook_settle.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
package keeper
import (
"fmt"
"github.com/spf13/cast"
sdkerrors "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrtypes "github.com/cosmos/cosmos-sdk/types/errors"
housetypes "github.com/sge-network/sge/x/house/types"
markettypes "github.com/sge-network/sge/x/market/types"
"github.com/sge-network/sge/x/orderbook/types"
)
// BatchOrderBookSettlements settles order books
func (k Keeper) BatchOrderBookSettlements(ctx sdk.Context) error {
toFetch := k.GetParams(ctx).BatchSettlementCount
unresolvedOrderBookIndex := 0
for toFetch > 0 {
// get the first resolved orderbook to process corresponding active deposits.
orderBookUID, found := k.GetFirstUnsettledResolvedOrderBook(ctx, unresolvedOrderBookIndex)
// return if there is no resolved orderbook.
if !found {
return nil
}
book, found := k.GetOrderBook(ctx, orderBookUID)
if !found {
return fmt.Errorf("orderbook not found %s", orderBookUID)
}
if book.Status != types.OrderBookStatus_ORDER_BOOK_STATUS_STATUS_RESOLVED {
return fmt.Errorf("orderbook status not resolved %s", orderBookUID)
}
market, found := k.marketKeeper.GetMarket(ctx, orderBookUID)
if !found {
return fmt.Errorf("market not found %s", orderBookUID)
}
// settle order book active deposits.
allSettled, settledCount, err := k.batchSettlementOfParticipation(ctx, orderBookUID, market, toFetch)
if err != nil {
return fmt.Errorf("could not settle orderbook %s %s", orderBookUID, err)
}
// if there is not any active deposit for orderbook
// we need to remove its uid from the list of unsettled resolved orderbooks.
if allSettled {
k.RemoveUnsettledResolvedOrderBook(ctx, orderBookUID)
book.Status = types.OrderBookStatus_ORDER_BOOK_STATUS_STATUS_SETTLED
k.SetOrderBook(ctx, book)
} else {
// update market index to be checked in the next loop.
unresolvedOrderBookIndex++
}
// update counter of bets to be processed in the next iteration.
toFetch -= settledCount
}
return nil
}
// batchSettlementOfParticipation settles active deposits of an orderbook
func (k Keeper) batchSettlementOfParticipation(
ctx sdk.Context,
orderBookUID string,
market markettypes.Market,
countToBeSettled uint64,
) (allSettled bool, settledCount uint64, err error) {
// initialize iterator for the certain number of active deposits
// equal to countToBeSettled
bookParticipations, err := k.GetParticipationsOfOrderBook(ctx, orderBookUID)
if err != nil {
return false, settledCount, fmt.Errorf("batch settlement of book %s failed: %s", orderBookUID, err)
}
processed := 0
for _, bookParticipation := range bookParticipations {
processed++
if !bookParticipation.IsSettled {
err = k.settleParticipation(ctx, bookParticipation, market)
if err != nil {
return allSettled, settledCount, fmt.Errorf(
"failed to settle deposit of batch settlement for participation %#v: %s",
bookParticipation,
err,
)
}
settledCount++
}
if cast.ToUint64(settledCount) >= countToBeSettled {
break
}
}
if len(bookParticipations) == processed {
allSettled = true
}
return allSettled, settledCount, nil
}
func (k Keeper) settleParticipation(
ctx sdk.Context,
bp types.OrderBookParticipation,
market markettypes.Market,
) error {
if bp.IsSettled {
return sdkerrors.Wrapf(
types.ErrBookParticipationAlreadySettled,
"%s %d",
bp.OrderBookUID,
bp.Index,
)
}
depositorAddress, err := sdk.AccAddressFromBech32(bp.ParticipantAddress)
if err != nil {
return sdkerrors.Wrapf(sdkerrtypes.ErrInvalidAddress, types.ErrTextInvalidDepositor, err)
}
refundHouseDepositFeeToDepositor := false
switch market.Status {
case markettypes.MarketStatus_MARKET_STATUS_RESULT_DECLARED:
bp.ReturnedAmount = bp.Liquidity.Add(bp.ActualProfit)
// refund participant's account from orderbook liquidity pool.
if err := k.refund(types.OrderBookLiquidityFunder{}, ctx, depositorAddress, bp.ReturnedAmount); err != nil {
return err
}
if bp.NotParticipatedInBetFulfillment() {
refundHouseDepositFeeToDepositor = true
}
if bp.ActualProfit.IsNegative() {
k.hooks.AfterHouseLoss(ctx, depositorAddress, bp.Liquidity, bp.ActualProfit.Abs())
} else {
k.hooks.AfterHouseWin(ctx, depositorAddress, bp.Liquidity, bp.ActualProfit)
}
case markettypes.MarketStatus_MARKET_STATUS_CANCELED,
markettypes.MarketStatus_MARKET_STATUS_ABORTED:
bp.ReturnedAmount = bp.Liquidity
// refund participant's account from orderbook liquidity pool.
if err := k.refund(types.OrderBookLiquidityFunder{}, ctx, depositorAddress, bp.ReturnedAmount); err != nil {
return err
}
refundHouseDepositFeeToDepositor = true
k.hooks.AfterHouseRefund(ctx, depositorAddress, bp.ReturnedAmount)
default:
return sdkerrors.Wrapf(
types.ErrUnknownMarketStatus,
"order book %s, market status %s",
bp.OrderBookUID,
market.Status,
)
}
if refundHouseDepositFeeToDepositor {
// refund participant's account from house fee collector.
if err := k.refund(housetypes.HouseFeeCollectorFunder{}, ctx, depositorAddress, bp.Fee); err != nil {
return err
}
bp.ReimbursedFee = bp.Fee
bp.ReturnedAmount = bp.ReturnedAmount.Add(bp.ReimbursedFee)
k.hooks.AfterHouseFeeRefund(ctx, depositorAddress, bp.Fee)
} else {
// refund participant's account from house fee collector.
if err := k.refund(housetypes.HouseFeeCollectorFunder{}, ctx, sdk.MustAccAddressFromBech32(market.Creator), bp.Fee); err != nil {
return err
}
}
// market uid
// participation index
// returned fees
// returned liquidity
// actual profit
bp.IsSettled = true
k.SetOrderBookParticipation(ctx, bp)
ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.TypeParticipationSettlement,
sdk.NewAttribute(types.AttributeValueMarketUID, market.UID),
sdk.NewAttribute(types.AttributeValueParticipationIndex, cast.ToString(bp.Index)),
sdk.NewAttribute(types.AttributeValueParticipationReimbursedFees, bp.ReimbursedFee.String()),
sdk.NewAttribute(types.AttributeValueParticipationReturnedAmount, bp.ReturnedAmount.String()),
sdk.NewAttribute(types.AttributeValueParticipationActualProfit, bp.ActualProfit.String()),
),
)
return nil
}