Skip to content

Commit 9777dca

Browse files
authored
Merge pull request #715 from neutron-org/feat/dex-state-testing-all-tests
feat: dex state testing all tests
2 parents 21e0680 + 57ada75 commit 9777dca

7 files changed

+2036
-0
lines changed
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
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

Comments
 (0)