-
Notifications
You must be signed in to change notification settings - Fork 13
/
swap.go
147 lines (121 loc) · 6.03 KB
/
swap.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
package keeper
import (
"fmt"
"strconv"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/gravity-devs/liquidity/x/liquidity/types"
)
// Execute Swap of the pool batch, Collect swap messages in batch for transact the same price for each batch and run them on endblock.
func (k Keeper) SwapExecution(ctx sdk.Context, poolBatch types.PoolBatch) (uint64, error) {
// get all swap message batch states that are not executed, not succeeded, and not to be deleted.
swapMsgStates := k.GetAllNotProcessedPoolBatchSwapMsgStates(ctx, poolBatch)
if len(swapMsgStates) == 0 {
return 0, nil
}
pool, found := k.GetPool(ctx, poolBatch.PoolId)
if !found {
return 0, types.ErrPoolNotExists
}
if k.IsDepletedPool(ctx, pool) {
return 0, types.ErrDepletedPool
}
currentHeight := ctx.BlockHeight()
// set executed states of all messages to true
executedMsgCount := uint64(0)
var swapMsgStatesNotToBeDeleted []*types.SwapMsgState
for _, sms := range swapMsgStates {
sms.Executed = true
executedMsgCount++
if currentHeight > sms.OrderExpiryHeight {
sms.ToBeDeleted = true
}
if err := k.ValidateMsgSwapWithinBatch(ctx, *sms.Msg, pool); err != nil {
sms.ToBeDeleted = true
}
if !sms.ToBeDeleted {
swapMsgStatesNotToBeDeleted = append(swapMsgStatesNotToBeDeleted, sms)
} else {
ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeSwapTransacted,
sdk.NewAttribute(types.AttributeValuePoolId, strconv.FormatUint(pool.Id, 10)),
sdk.NewAttribute(types.AttributeValueBatchIndex, strconv.FormatUint(poolBatch.Index, 10)),
sdk.NewAttribute(types.AttributeValueMsgIndex, strconv.FormatUint(sms.MsgIndex, 10)),
sdk.NewAttribute(types.AttributeValueSwapRequester, sms.Msg.GetSwapRequester().String()),
sdk.NewAttribute(types.AttributeValueSwapTypeId, strconv.FormatUint(uint64(sms.Msg.SwapTypeId), 10)),
sdk.NewAttribute(types.AttributeValueOfferCoinDenom, sms.Msg.OfferCoin.Denom),
sdk.NewAttribute(types.AttributeValueOfferCoinAmount, sms.Msg.OfferCoin.Amount.String()),
sdk.NewAttribute(types.AttributeValueDemandCoinDenom, sms.Msg.DemandCoinDenom),
sdk.NewAttribute(types.AttributeValueOrderPrice, sms.Msg.OrderPrice.String()),
sdk.NewAttribute(types.AttributeValueRemainingOfferCoinAmount, sms.RemainingOfferCoin.Amount.String()),
sdk.NewAttribute(types.AttributeValueExchangedOfferCoinAmount, sms.ExchangedOfferCoin.Amount.String()),
sdk.NewAttribute(types.AttributeValueReservedOfferCoinFeeAmount, sms.ReservedOfferCoinFee.Amount.String()),
sdk.NewAttribute(types.AttributeValueOrderExpiryHeight, strconv.FormatInt(sms.OrderExpiryHeight, 10)),
sdk.NewAttribute(types.AttributeValueSuccess, types.Failure),
))
}
}
k.SetPoolBatchSwapMsgStatesByPointer(ctx, pool.Id, swapMsgStates)
swapMsgStates = swapMsgStatesNotToBeDeleted
types.ValidateStateAndExpireOrders(swapMsgStates, currentHeight, false)
// get reserve coins from the liquidity pool and calculate the current pool price (p = x / y)
reserveCoins := k.GetReserveCoins(ctx, pool)
X := reserveCoins[0].Amount.ToDec()
Y := reserveCoins[1].Amount.ToDec()
currentPoolPrice := X.Quo(Y)
denomX := reserveCoins[0].Denom
denomY := reserveCoins[1].Denom
// make orderMap, orderbook by sort orderMap
orderMap, xToY, yToX := types.MakeOrderMap(swapMsgStates, denomX, denomY, false)
orderBook := orderMap.SortOrderBook()
// check orderbook validity and compute batchResult(direction, swapPrice, ..)
result, found := orderBook.Match(X, Y)
if !found || X.Quo(Y).IsZero() {
err := k.RefundSwaps(ctx, pool, swapMsgStates)
return executedMsgCount, err
}
// find order match, calculate pool delta with the total x, y amounts for the invariant check
var matchResultXtoY, matchResultYtoX []types.MatchResult
poolXDelta := sdk.ZeroDec()
poolYDelta := sdk.ZeroDec()
if result.MatchType != types.NoMatch {
var poolXDeltaXtoY, poolXDeltaYtoX, poolYDeltaYtoX, poolYDeltaXtoY sdk.Dec
matchResultXtoY, poolXDeltaXtoY, poolYDeltaXtoY = types.FindOrderMatch(types.DirectionXtoY, xToY, result.EX, result.SwapPrice, currentHeight)
matchResultYtoX, poolXDeltaYtoX, poolYDeltaYtoX = types.FindOrderMatch(types.DirectionYtoX, yToX, result.EY, result.SwapPrice, currentHeight)
poolXDelta = poolXDeltaXtoY.Add(poolXDeltaYtoX)
poolYDelta = poolYDeltaXtoY.Add(poolYDeltaYtoX)
}
xToY, yToX, X, Y, poolXDelta2, poolYDelta2 := types.UpdateSwapMsgStates(X, Y, xToY, yToX, matchResultXtoY, matchResultYtoX)
lastPrice := X.Quo(Y)
if BatchLogicInvariantCheckFlag {
SwapMatchingInvariants(xToY, yToX, matchResultXtoY, matchResultYtoX)
SwapPriceInvariants(matchResultXtoY, matchResultYtoX, poolXDelta, poolYDelta, poolXDelta2, poolYDelta2, result)
}
types.ValidateStateAndExpireOrders(xToY, currentHeight, false)
types.ValidateStateAndExpireOrders(yToX, currentHeight, false)
orderMapExecuted, _, _ := types.MakeOrderMap(append(xToY, yToX...), denomX, denomY, true)
orderBookExecuted := orderMapExecuted.SortOrderBook()
if !orderBookExecuted.Validate(lastPrice) {
return executedMsgCount, types.ErrOrderBookInvalidity
}
types.ValidateStateAndExpireOrders(xToY, currentHeight, true)
types.ValidateStateAndExpireOrders(yToX, currentHeight, true)
// make index map for match result
matchResultMap := make(map[uint64]types.MatchResult)
for _, match := range append(matchResultXtoY, matchResultYtoX...) {
if _, ok := matchResultMap[match.SwapMsgState.MsgIndex]; ok {
return executedMsgCount, fmt.Errorf("duplicate match order")
}
matchResultMap[match.SwapMsgState.MsgIndex] = match
}
if BatchLogicInvariantCheckFlag {
SwapPriceDirectionInvariants(currentPoolPrice, result)
SwapMsgStatesInvariants(matchResultXtoY, matchResultYtoX, matchResultMap, swapMsgStates, xToY, yToX)
SwapOrdersExecutionStateInvariants(matchResultMap, swapMsgStates, result, denomX)
}
// execute transact, refund, expire, send coins with escrow, update state by TransactAndRefundSwapLiquidityPool
if err := k.TransactAndRefundSwapLiquidityPool(ctx, swapMsgStates, matchResultMap, pool, result); err != nil {
return executedMsgCount, err
}
return executedMsgCount, nil
}