github.com/gravity-devs/liquidity@v1.5.3/x/liquidity/keeper/swap.go (about) 1 package keeper 2 3 import ( 4 "fmt" 5 "strconv" 6 7 sdk "github.com/cosmos/cosmos-sdk/types" 8 9 "github.com/gravity-devs/liquidity/x/liquidity/types" 10 ) 11 12 // Execute Swap of the pool batch, Collect swap messages in batch for transact the same price for each batch and run them on endblock. 13 func (k Keeper) SwapExecution(ctx sdk.Context, poolBatch types.PoolBatch) (uint64, error) { 14 // get all swap message batch states that are not executed, not succeeded, and not to be deleted. 15 swapMsgStates := k.GetAllNotProcessedPoolBatchSwapMsgStates(ctx, poolBatch) 16 if len(swapMsgStates) == 0 { 17 return 0, nil 18 } 19 20 pool, found := k.GetPool(ctx, poolBatch.PoolId) 21 if !found { 22 return 0, types.ErrPoolNotExists 23 } 24 25 if k.IsDepletedPool(ctx, pool) { 26 return 0, types.ErrDepletedPool 27 } 28 29 currentHeight := ctx.BlockHeight() 30 // set executed states of all messages to true 31 executedMsgCount := uint64(0) 32 var swapMsgStatesNotToBeDeleted []*types.SwapMsgState 33 for _, sms := range swapMsgStates { 34 sms.Executed = true 35 executedMsgCount++ 36 if currentHeight > sms.OrderExpiryHeight { 37 sms.ToBeDeleted = true 38 } 39 if err := k.ValidateMsgSwapWithinBatch(ctx, *sms.Msg, pool); err != nil { 40 sms.ToBeDeleted = true 41 } 42 if !sms.ToBeDeleted { 43 swapMsgStatesNotToBeDeleted = append(swapMsgStatesNotToBeDeleted, sms) 44 } else { 45 ctx.EventManager().EmitEvent( 46 sdk.NewEvent( 47 types.EventTypeSwapTransacted, 48 sdk.NewAttribute(types.AttributeValuePoolId, strconv.FormatUint(pool.Id, 10)), 49 sdk.NewAttribute(types.AttributeValueBatchIndex, strconv.FormatUint(poolBatch.Index, 10)), 50 sdk.NewAttribute(types.AttributeValueMsgIndex, strconv.FormatUint(sms.MsgIndex, 10)), 51 sdk.NewAttribute(types.AttributeValueSwapRequester, sms.Msg.GetSwapRequester().String()), 52 sdk.NewAttribute(types.AttributeValueSwapTypeId, strconv.FormatUint(uint64(sms.Msg.SwapTypeId), 10)), 53 sdk.NewAttribute(types.AttributeValueOfferCoinDenom, sms.Msg.OfferCoin.Denom), 54 sdk.NewAttribute(types.AttributeValueOfferCoinAmount, sms.Msg.OfferCoin.Amount.String()), 55 sdk.NewAttribute(types.AttributeValueDemandCoinDenom, sms.Msg.DemandCoinDenom), 56 sdk.NewAttribute(types.AttributeValueOrderPrice, sms.Msg.OrderPrice.String()), 57 sdk.NewAttribute(types.AttributeValueRemainingOfferCoinAmount, sms.RemainingOfferCoin.Amount.String()), 58 sdk.NewAttribute(types.AttributeValueExchangedOfferCoinAmount, sms.ExchangedOfferCoin.Amount.String()), 59 sdk.NewAttribute(types.AttributeValueReservedOfferCoinFeeAmount, sms.ReservedOfferCoinFee.Amount.String()), 60 sdk.NewAttribute(types.AttributeValueOrderExpiryHeight, strconv.FormatInt(sms.OrderExpiryHeight, 10)), 61 sdk.NewAttribute(types.AttributeValueSuccess, types.Failure), 62 )) 63 } 64 } 65 k.SetPoolBatchSwapMsgStatesByPointer(ctx, pool.Id, swapMsgStates) 66 swapMsgStates = swapMsgStatesNotToBeDeleted 67 68 types.ValidateStateAndExpireOrders(swapMsgStates, currentHeight, false) 69 70 // get reserve coins from the liquidity pool and calculate the current pool price (p = x / y) 71 reserveCoins := k.GetReserveCoins(ctx, pool) 72 73 X := reserveCoins[0].Amount.ToDec() 74 Y := reserveCoins[1].Amount.ToDec() 75 currentPoolPrice := X.Quo(Y) 76 denomX := reserveCoins[0].Denom 77 denomY := reserveCoins[1].Denom 78 79 // make orderMap, orderbook by sort orderMap 80 orderMap, xToY, yToX := types.MakeOrderMap(swapMsgStates, denomX, denomY, false) 81 orderBook := orderMap.SortOrderBook() 82 83 // check orderbook validity and compute batchResult(direction, swapPrice, ..) 84 result, found := orderBook.Match(X, Y) 85 86 if !found || X.Quo(Y).IsZero() { 87 err := k.RefundSwaps(ctx, pool, swapMsgStates) 88 return executedMsgCount, err 89 } 90 91 // find order match, calculate pool delta with the total x, y amounts for the invariant check 92 var matchResultXtoY, matchResultYtoX []types.MatchResult 93 94 poolXDelta := sdk.ZeroDec() 95 poolYDelta := sdk.ZeroDec() 96 97 if result.MatchType != types.NoMatch { 98 var poolXDeltaXtoY, poolXDeltaYtoX, poolYDeltaYtoX, poolYDeltaXtoY sdk.Dec 99 matchResultXtoY, poolXDeltaXtoY, poolYDeltaXtoY = types.FindOrderMatch(types.DirectionXtoY, xToY, result.EX, result.SwapPrice, currentHeight) 100 matchResultYtoX, poolXDeltaYtoX, poolYDeltaYtoX = types.FindOrderMatch(types.DirectionYtoX, yToX, result.EY, result.SwapPrice, currentHeight) 101 poolXDelta = poolXDeltaXtoY.Add(poolXDeltaYtoX) 102 poolYDelta = poolYDeltaXtoY.Add(poolYDeltaYtoX) 103 } 104 105 xToY, yToX, X, Y, poolXDelta2, poolYDelta2 := types.UpdateSwapMsgStates(X, Y, xToY, yToX, matchResultXtoY, matchResultYtoX) 106 107 lastPrice := X.Quo(Y) 108 109 if BatchLogicInvariantCheckFlag { 110 SwapMatchingInvariants(xToY, yToX, matchResultXtoY, matchResultYtoX) 111 SwapPriceInvariants(matchResultXtoY, matchResultYtoX, poolXDelta, poolYDelta, poolXDelta2, poolYDelta2, result) 112 } 113 114 types.ValidateStateAndExpireOrders(xToY, currentHeight, false) 115 types.ValidateStateAndExpireOrders(yToX, currentHeight, false) 116 117 orderMapExecuted, _, _ := types.MakeOrderMap(append(xToY, yToX...), denomX, denomY, true) 118 orderBookExecuted := orderMapExecuted.SortOrderBook() 119 if !orderBookExecuted.Validate(lastPrice) { 120 return executedMsgCount, types.ErrOrderBookInvalidity 121 } 122 123 types.ValidateStateAndExpireOrders(xToY, currentHeight, true) 124 types.ValidateStateAndExpireOrders(yToX, currentHeight, true) 125 126 // make index map for match result 127 matchResultMap := make(map[uint64]types.MatchResult) 128 for _, match := range append(matchResultXtoY, matchResultYtoX...) { 129 if _, ok := matchResultMap[match.SwapMsgState.MsgIndex]; ok { 130 return executedMsgCount, fmt.Errorf("duplicate match order") 131 } 132 matchResultMap[match.SwapMsgState.MsgIndex] = match 133 } 134 135 if BatchLogicInvariantCheckFlag { 136 SwapPriceDirectionInvariants(currentPoolPrice, result) 137 SwapMsgStatesInvariants(matchResultXtoY, matchResultYtoX, matchResultMap, swapMsgStates, xToY, yToX) 138 SwapOrdersExecutionStateInvariants(matchResultMap, swapMsgStates, result, denomX) 139 } 140 141 // execute transact, refund, expire, send coins with escrow, update state by TransactAndRefundSwapLiquidityPool 142 if err := k.TransactAndRefundSwapLiquidityPool(ctx, swapMsgStates, matchResultMap, pool, result); err != nil { 143 return executedMsgCount, err 144 } 145 146 return executedMsgCount, nil 147 }