code.vegaprotocol.io/vega@v0.79.0/core/liquidity/v2/engine_test.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package liquidity_test 17 18 import ( 19 "context" 20 "testing" 21 "time" 22 23 "code.vegaprotocol.io/vega/core/events" 24 "code.vegaprotocol.io/vega/core/idgeneration" 25 "code.vegaprotocol.io/vega/core/types" 26 "code.vegaprotocol.io/vega/libs/crypto" 27 "code.vegaprotocol.io/vega/libs/num" 28 commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1" 29 30 "github.com/golang/mock/gomock" 31 "github.com/stretchr/testify/assert" 32 "github.com/stretchr/testify/require" 33 ) 34 35 func TestSubmissions(t *testing.T) { 36 t.Run("Create and cancel", testSubmissionCreateAndCancel) 37 t.Run("Cancel non existing", testCancelNonExistingSubmission) 38 t.Run("Can to submit when current or pending LP exists", testFailsWhenLPExists) 39 } 40 41 func testFailsWhenLPExists(t *testing.T) { 42 var ( 43 party = "party-1" 44 ctx = context.Background() 45 te = newTestEngine(t) 46 ) 47 defer te.ctrl.Finish() 48 49 require.Nil(t, te.engine.LiquidityProvisionByPartyID("some-party")) 50 51 lps1 := &commandspb.LiquidityProvisionSubmission{ 52 MarketId: te.marketID, CommitmentAmount: "100", Fee: "0.5", 53 } 54 lps, err := types.LiquidityProvisionSubmissionFromProto(lps1) 55 require.NoError(t, err) 56 57 deterministicID := crypto.RandomHash() 58 idGen := idgeneration.New(deterministicID) 59 60 lpID := idGen.NextID() 61 now := te.tsvc.GetTimeNow() 62 nowNano := now.UnixNano() 63 64 expected := &types.LiquidityProvision{ 65 ID: lpID, 66 MarketID: te.marketID, 67 Party: party, 68 Fee: num.DecimalFromFloat(0.5), 69 CommitmentAmount: lps.CommitmentAmount.Clone(), 70 CreatedAt: nowNano, 71 UpdatedAt: nowNano, 72 Status: types.LiquidityProvisionStatusActive, 73 Version: 1, 74 } 75 76 // Creating a submission should fire an event 77 te.broker.EXPECT().Send(gomock.Any()).AnyTimes() 78 te.auctionState.EXPECT().IsOpeningAuction().Return(false).AnyTimes() 79 80 idgen := idgeneration.New(deterministicID) 81 _, err = te.engine.SubmitLiquidityProvision(ctx, lps, party, idgen) 82 require.NoError(t, err) 83 84 // first validate that the amendment is pending 85 pendingLp := te.engine.PendingProvisionByPartyID(party) 86 assert.Equal(t, expected.CommitmentAmount.String(), pendingLp.CommitmentAmount.String()) 87 assert.Equal(t, expected.Fee.String(), pendingLp.Fee.String()) 88 89 idgen = idgeneration.New(deterministicID) 90 91 lps.CommitmentAmount = num.NewUint(1000) 92 _, err = te.engine.SubmitLiquidityProvision(ctx, lps, party, idgen) 93 require.Error(t, err, "liquidity provision already exists") 94 } 95 96 func testSubmissionCreateAndCancel(t *testing.T) { 97 var ( 98 party = "party-1" 99 ctx = context.Background() 100 te = newTestEngine(t) 101 ) 102 defer te.ctrl.Finish() 103 104 require.Nil(t, te.engine.LiquidityProvisionByPartyID("some-party")) 105 106 lps1 := &commandspb.LiquidityProvisionSubmission{ 107 MarketId: te.marketID, CommitmentAmount: "100", Fee: "0.5", 108 } 109 lps, err := types.LiquidityProvisionSubmissionFromProto(lps1) 110 require.NoError(t, err) 111 112 deterministicID := crypto.RandomHash() 113 idGen := idgeneration.New(deterministicID) 114 115 lpID := idGen.NextID() 116 now := te.tsvc.GetTimeNow() 117 nowNano := now.UnixNano() 118 119 expected := &types.LiquidityProvision{ 120 ID: lpID, 121 MarketID: te.marketID, 122 Party: party, 123 Fee: num.DecimalFromFloat(0.5), 124 CommitmentAmount: lps.CommitmentAmount.Clone(), 125 CreatedAt: nowNano, 126 UpdatedAt: nowNano, 127 Status: types.LiquidityProvisionStatusActive, 128 Version: 1, 129 } 130 131 // Creating a submission should fire an event 132 te.broker.EXPECT().Send(gomock.Any()).AnyTimes() 133 te.auctionState.EXPECT().IsOpeningAuction().Return(false).AnyTimes() 134 135 idgen := idgeneration.New(deterministicID) 136 _, err = te.engine.SubmitLiquidityProvision(ctx, lps, party, idgen) 137 require.NoError(t, err) 138 139 // first validate that the amendment is pending 140 pendingLp := te.engine.PendingProvisionByPartyID(party) 141 assert.Equal(t, expected.CommitmentAmount.String(), pendingLp.CommitmentAmount.String()) 142 assert.Equal(t, expected.Fee.String(), pendingLp.Fee.String()) 143 144 got := te.engine.LiquidityProvisionByPartyID(party) 145 require.Nil(t, got) 146 147 zero := num.UintZero() 148 149 te.engine.ResetSLAEpoch(now, zero, zero, num.DecimalZero()) 150 te.engine.ApplyPendingProvisions(ctx, now) 151 152 got = te.engine.LiquidityProvisionByPartyID(party) 153 require.Equal(t, expected.CommitmentAmount.String(), got.CommitmentAmount.String()) 154 require.Equal(t, expected.Fee, got.Fee) 155 require.Equal(t, expected.Version, got.Version) 156 157 expected.Status = types.LiquidityProvisionStatusCancelled 158 te.broker.EXPECT().Send( 159 events.NewLiquidityProvisionEvent(ctx, expected), 160 ).AnyTimes() 161 162 err = te.engine.CancelLiquidityProvision(ctx, party) 163 require.NoError(t, err) 164 require.Nil(t, te.engine.LiquidityProvisionByPartyID(party), 165 "Party '%s' should not be a LiquidityProvider after Committing 0 amount", party) 166 } 167 168 func testCancelNonExistingSubmission(t *testing.T) { 169 var ( 170 party = "party-1" 171 ctx = context.Background() 172 tng = newTestEngine(t) 173 ) 174 defer tng.ctrl.Finish() 175 176 err := tng.engine.CancelLiquidityProvision(ctx, party) 177 require.Error(t, err) 178 } 179 180 func TestCalculateSuppliedStake(t *testing.T) { 181 var ( 182 party1 = "party-1" 183 party2 = "party-2" 184 party3 = "party-3" 185 ctx = context.Background() 186 tng = newTestEngine(t) 187 ) 188 defer tng.ctrl.Finish() 189 190 // We don't care about the following calls 191 tng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 192 tng.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() 193 tng.auctionState.EXPECT().IsOpeningAuction().Return(false).AnyTimes() 194 195 zero := num.UintZero() 196 tng.orderbook.EXPECT().GetBestStaticBidPrice().Return(zero, nil).AnyTimes() 197 tng.orderbook.EXPECT().GetBestStaticAskPrice().Return(zero, nil).AnyTimes() 198 199 tng.auctionState.EXPECT().InAuction().Return(false).AnyTimes() 200 201 // Send a submission 202 lp1pb := &commandspb.LiquidityProvisionSubmission{ 203 MarketId: tng.marketID, CommitmentAmount: "100", Fee: "0.5", 204 } 205 lp1, err := types.LiquidityProvisionSubmissionFromProto(lp1pb) 206 require.NoError(t, err) 207 208 idgen := idgeneration.New(crypto.RandomHash()) 209 _, err = tng.engine.SubmitLiquidityProvision(ctx, lp1, party1, idgen) 210 require.NoError(t, err) 211 212 now := tng.tsvc.GetTimeNow() 213 214 tng.engine.ApplyPendingProvisions(ctx, now) 215 tng.engine.ResetSLAEpoch(time.Now(), zero, zero, num.DecimalOne()) 216 217 suppliedStake := tng.engine.CalculateSuppliedStake() 218 require.Equal(t, lp1.CommitmentAmount, suppliedStake) 219 220 lp2pb := &commandspb.LiquidityProvisionSubmission{ 221 MarketId: tng.marketID, CommitmentAmount: "500", Fee: "0.5", 222 } 223 lp2, err := types.LiquidityProvisionSubmissionFromProto(lp2pb) 224 require.NoError(t, err) 225 226 idgen = idgeneration.New(crypto.RandomHash()) 227 _, err = tng.engine.SubmitLiquidityProvision(ctx, lp2, party2, idgen) 228 require.NoError(t, err) 229 230 tng.engine.ResetSLAEpoch(now, zero, zero, num.DecimalZero()) 231 tng.engine.ApplyPendingProvisions(ctx, now) 232 233 suppliedStake = tng.engine.CalculateSuppliedStake() 234 require.Equal(t, num.Sum(lp1.CommitmentAmount, lp2.CommitmentAmount), suppliedStake) 235 236 lp3pb := &commandspb.LiquidityProvisionSubmission{ 237 MarketId: tng.marketID, CommitmentAmount: "962", Fee: "0.5", 238 } 239 lp3, err := types.LiquidityProvisionSubmissionFromProto(lp3pb) 240 require.NoError(t, err) 241 242 idgen = idgeneration.New(crypto.RandomHash()) 243 _, err = tng.engine.SubmitLiquidityProvision(ctx, lp3, party3, idgen) 244 require.NoError(t, err) 245 246 suppliedStake = tng.engine.CalculateSuppliedStake() 247 require.Equal(t, num.Sum(lp1.CommitmentAmount, lp2.CommitmentAmount, lp3.CommitmentAmount), suppliedStake) 248 249 err = tng.engine.CancelLiquidityProvision(ctx, party1) 250 require.NoError(t, err) 251 suppliedStake = tng.engine.CalculateSuppliedStake() 252 require.Equal(t, num.Sum(lp2.CommitmentAmount, lp3.CommitmentAmount), suppliedStake) 253 }