|
| 1 | +package dex_state_test |
| 2 | + |
| 3 | +import ( |
| 4 | + "errors" |
| 5 | + "strconv" |
| 6 | + "testing" |
| 7 | + "time" |
| 8 | + |
| 9 | + sdk "github.com/cosmos/cosmos-sdk/types" |
| 10 | + |
| 11 | + math_utils "github.com/neutron-org/neutron/v5/utils/math" |
| 12 | + dextypes "github.com/neutron-org/neutron/v5/x/dex/types" |
| 13 | +) |
| 14 | + |
| 15 | +type cancelLimitOrderTestParams struct { |
| 16 | + // State Conditions |
| 17 | + SharedParams |
| 18 | + ExistingTokenAHolders string |
| 19 | + Filled int64 |
| 20 | + WithdrawnCreator bool |
| 21 | + WithdrawnOneOther bool |
| 22 | + Expired bool |
| 23 | + OrderType dextypes.LimitOrderType // JIT, GTT, GTC |
| 24 | +} |
| 25 | + |
| 26 | +func (p cancelLimitOrderTestParams) printTestInfo(t *testing.T) { |
| 27 | + t.Logf(` |
| 28 | + Existing Shareholders: %s |
| 29 | + Filled: %v |
| 30 | + WithdrawnCreator: %v |
| 31 | + WithdrawnOneOther: %t |
| 32 | + Expired: %t |
| 33 | + OrderType: %v`, |
| 34 | + p.ExistingTokenAHolders, |
| 35 | + p.Filled, |
| 36 | + p.WithdrawnCreator, |
| 37 | + p.WithdrawnOneOther, |
| 38 | + p.Expired, |
| 39 | + p.OrderType.String(), |
| 40 | + ) |
| 41 | +} |
| 42 | + |
| 43 | +func hydrateCancelLoTestCase(params map[string]string) cancelLimitOrderTestParams { |
| 44 | + selltick, err := dextypes.CalcTickIndexFromPrice(math_utils.MustNewPrecDecFromStr(DefaultSellPrice)) |
| 45 | + if err != nil { |
| 46 | + panic(err) |
| 47 | + } |
| 48 | + c := cancelLimitOrderTestParams{ |
| 49 | + ExistingTokenAHolders: params["ExistingTokenAHolders"], |
| 50 | + Filled: int64(parseInt(params["Filled"])), |
| 51 | + WithdrawnCreator: parseBool(params["WithdrawnCreator"]), |
| 52 | + WithdrawnOneOther: parseBool(params["WithdrawnOneOther"]), |
| 53 | + Expired: parseBool(params["Expired"]), |
| 54 | + OrderType: dextypes.LimitOrderType(dextypes.LimitOrderType_value[params["OrderType"]]), |
| 55 | + } |
| 56 | + c.SharedParams.Tick = selltick |
| 57 | + return c |
| 58 | +} |
| 59 | + |
| 60 | +func (s *DexStateTestSuite) setupCancelTest(params cancelLimitOrderTestParams) (tranche *dextypes.LimitOrderTranche) { |
| 61 | + coinA := sdk.NewCoin(params.PairID.Token0, BaseTokenAmountInt) |
| 62 | + coinB := sdk.NewCoin(params.PairID.Token1, BaseTokenAmountInt.MulRaw(10)) |
| 63 | + s.FundAcc(s.creator, sdk.NewCoins(coinA)) |
| 64 | + var expTime *time.Time |
| 65 | + if params.OrderType.IsGoodTil() { |
| 66 | + t := time.Now() |
| 67 | + expTime = &t |
| 68 | + } |
| 69 | + res := s.makePlaceLOSuccess(s.creator, coinA, coinB.Denom, DefaultSellPrice, params.OrderType, expTime) |
| 70 | + |
| 71 | + totalDeposited := BaseTokenAmountInt |
| 72 | + if params.ExistingTokenAHolders == OneOtherAndCreatorLO { |
| 73 | + totalDeposited = totalDeposited.MulRaw(2) |
| 74 | + s.FundAcc(s.alice, sdk.NewCoins(coinA)) |
| 75 | + s.makePlaceLOSuccess(s.alice, coinA, coinB.Denom, DefaultSellPrice, params.OrderType, expTime) |
| 76 | + } |
| 77 | + |
| 78 | + if params.Filled > 0 { |
| 79 | + s.FundAcc(s.bob, sdk.NewCoins(coinB)) |
| 80 | + fillAmount := totalDeposited.MulRaw(params.Filled).QuoRaw(100) |
| 81 | + _, err := s.makePlaceTakerLO(s.bob, coinB, coinA.Denom, DefaultBuyPriceTaker, dextypes.LimitOrderType_IMMEDIATE_OR_CANCEL, &fillAmount) |
| 82 | + s.NoError(err) |
| 83 | + } |
| 84 | + |
| 85 | + if params.WithdrawnCreator { |
| 86 | + s.makeWithdrawFilledSuccess(s.creator, res.TrancheKey) |
| 87 | + } |
| 88 | + |
| 89 | + if params.WithdrawnOneOther { |
| 90 | + s.makeWithdrawFilledSuccess(s.alice, res.TrancheKey) |
| 91 | + } |
| 92 | + |
| 93 | + if params.Expired { |
| 94 | + s.App.DexKeeper.PurgeExpiredLimitOrders(s.Ctx, time.Now()) |
| 95 | + } |
| 96 | + |
| 97 | + req := dextypes.QueryGetLimitOrderTrancheRequest{ |
| 98 | + PairId: params.PairID.CanonicalString(), |
| 99 | + TickIndex: params.Tick, |
| 100 | + TokenIn: params.PairID.Token0, |
| 101 | + TrancheKey: res.TrancheKey, |
| 102 | + } |
| 103 | + tranchResp, err := s.App.DexKeeper.LimitOrderTranche(s.Ctx, &req) |
| 104 | + s.NoError(err) |
| 105 | + |
| 106 | + return tranchResp.LimitOrderTranche |
| 107 | +} |
| 108 | + |
| 109 | +func hydrateAllCancelLoTestCases(paramsList []map[string]string) []cancelLimitOrderTestParams { |
| 110 | + allTCs := make([]cancelLimitOrderTestParams, 0) |
| 111 | + for i, paramsRaw := range paramsList { |
| 112 | + tc := hydrateCancelLoTestCase(paramsRaw) |
| 113 | + |
| 114 | + pairID := generatePairID(i) |
| 115 | + tc.PairID = pairID |
| 116 | + |
| 117 | + allTCs = append(allTCs, tc) |
| 118 | + } |
| 119 | + |
| 120 | + return removeRedundantCancelLOTests(allTCs) |
| 121 | +} |
| 122 | + |
| 123 | +func removeRedundantCancelLOTests(params []cancelLimitOrderTestParams) []cancelLimitOrderTestParams { |
| 124 | + newParams := make([]cancelLimitOrderTestParams, 0) |
| 125 | + for _, p := range params { |
| 126 | + // it's impossible to withdraw 0 filled |
| 127 | + // error checks is not in a scope of the testcase (see withdraw filled test) |
| 128 | + if p.Filled == 0 && (p.WithdrawnOneOther || p.WithdrawnCreator) { |
| 129 | + continue |
| 130 | + } |
| 131 | + if p.Expired && p.OrderType.IsGTC() { |
| 132 | + continue |
| 133 | + } |
| 134 | + if p.WithdrawnOneOther && p.ExistingTokenAHolders == CreatorLO { |
| 135 | + continue |
| 136 | + } |
| 137 | + if p.ExistingTokenAHolders == OneOtherAndCreatorLO && !p.OrderType.IsGTC() { |
| 138 | + // user tranches combined into tranches only for LimitOrderType_GOOD_TIL_CANCELLED |
| 139 | + // it does not make any sense to create two tranches |
| 140 | + continue |
| 141 | + } |
| 142 | + newParams = append(newParams, p) |
| 143 | + } |
| 144 | + return newParams |
| 145 | +} |
| 146 | + |
| 147 | +func (s *DexStateTestSuite) handleCancelErrors(params cancelLimitOrderTestParams, err error) { |
| 148 | + if params.Filled == 100 && params.WithdrawnCreator { |
| 149 | + if errors.Is(dextypes.ErrValidLimitOrderTrancheNotFound, err) { |
| 150 | + s.T().Skip() |
| 151 | + } |
| 152 | + } |
| 153 | + s.NoError(err) |
| 154 | +} |
| 155 | + |
| 156 | +func (s *DexStateTestSuite) assertCalcelAmount(params cancelLimitOrderTestParams) { |
| 157 | + depositSize := BaseTokenAmountInt |
| 158 | + |
| 159 | + // expected balance: InitialBalance - depositSize + pre-withdrawn (filled/2 or 0) + withdrawn (filled/2 or filled) |
| 160 | + // pre-withdrawn (filled/2 or 0) + withdrawn (filled/2 or filled) === filled |
| 161 | + // converted to TokenB |
| 162 | + price := dextypes.MustCalcPrice(params.Tick) |
| 163 | + expectedBalanceB := price.MulInt(depositSize.MulRaw(params.Filled).QuoRaw(100)).Ceil().TruncateInt() |
| 164 | + expectedBalanceA := depositSize.Sub(depositSize.MulRaw(params.Filled).QuoRaw(100)) |
| 165 | + // 1 - withdrawn amount |
| 166 | + s.assertBalanceWithPrecision(s.creator, params.PairID.Token1, expectedBalanceB, 3) |
| 167 | + |
| 168 | + s.assertBalance(s.creator, params.PairID.Token0, expectedBalanceA) |
| 169 | +} |
| 170 | + |
| 171 | +func TestCancel(t *testing.T) { |
| 172 | + testParams := []testParams{ |
| 173 | + {field: "ExistingTokenAHolders", states: []string{CreatorLO, OneOtherAndCreatorLO}}, |
| 174 | + {field: "Filled", states: []string{ZeroPCT, FiftyPCT, HundredPct}}, |
| 175 | + {field: "WithdrawnCreator", states: []string{True, False}}, |
| 176 | + {field: "WithdrawnOneOther", states: []string{True, False}}, |
| 177 | + {field: "OrderType", states: []string{ |
| 178 | + dextypes.LimitOrderType_name[int32(dextypes.LimitOrderType_GOOD_TIL_CANCELLED)], |
| 179 | + dextypes.LimitOrderType_name[int32(dextypes.LimitOrderType_GOOD_TIL_TIME)], |
| 180 | + dextypes.LimitOrderType_name[int32(dextypes.LimitOrderType_JUST_IN_TIME)], |
| 181 | + }}, |
| 182 | + {field: "Expired", states: []string{True, False}}, |
| 183 | + } |
| 184 | + testCasesRaw := generatePermutations(testParams) |
| 185 | + testCases := hydrateAllCancelLoTestCases(testCasesRaw) |
| 186 | + |
| 187 | + s := new(DexStateTestSuite) |
| 188 | + s.SetT(t) |
| 189 | + s.SetupTest() |
| 190 | + |
| 191 | + for i, tc := range testCases { |
| 192 | + t.Run(strconv.Itoa(i), func(t *testing.T) { |
| 193 | + s.SetT(t) |
| 194 | + tc.printTestInfo(t) |
| 195 | + |
| 196 | + tranche := s.setupCancelTest(tc) |
| 197 | + |
| 198 | + _, err := s.makeCancel(s.creator, tranche.Key.TrancheKey) |
| 199 | + s.handleCancelErrors(tc, err) |
| 200 | + _, found := s.App.DexKeeper.GetLimitOrderTrancheUser(s.Ctx, s.creator.String(), tranche.Key.TrancheKey) |
| 201 | + s.False(found) |
| 202 | + s.assertCalcelAmount(tc) |
| 203 | + }) |
| 204 | + } |
| 205 | +} |
0 commit comments