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  }