code.vegaprotocol.io/vega@v0.79.0/core/collateral/network_mtm_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 collateral_test 17 18 import ( 19 "context" 20 "testing" 21 22 "code.vegaprotocol.io/vega/core/events" 23 "code.vegaprotocol.io/vega/core/types" 24 "code.vegaprotocol.io/vega/libs/num" 25 26 "github.com/golang/mock/gomock" 27 "github.com/stretchr/testify/assert" 28 "github.com/stretchr/testify/require" 29 ) 30 31 func TestMTMWithNetwork(t *testing.T) { 32 t.Run("test MTM with the network on the loss side (insurance pool has enough balance)", testMTMWithNetworkNoLossSoc) 33 t.Run("test MTM with network on the loss side (loss socialization)", testMTMWithNetworkLossSoc) 34 } 35 36 type PartyEvt interface { 37 Party() string 38 } 39 40 func testMTMWithNetworkNoLossSoc(t *testing.T) { 41 party := "test-party" 42 moneyParty := "money-party" 43 price := num.NewUint(1000) 44 45 eng := getTestEngine(t) 46 defer eng.Finish() 47 48 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 49 insurancePool, err := eng.GetMarketInsurancePoolAccount(testMarketID, testMarketAsset) 50 assert.Nil(t, err) 51 err = eng.UpdateBalance(context.Background(), insurancePool.ID, num.Sum(price, price)) 52 assert.Nil(t, err) 53 54 // create party accounts 55 eng.broker.EXPECT().Send(gomock.Any()).Times(8) 56 gID, _ := eng.Engine.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 57 mID, err := eng.Engine.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 58 assert.Nil(t, err) 59 60 assert.NotEmpty(t, mID) 61 assert.NotEmpty(t, gID) 62 63 // create + add balance 64 _, _ = eng.Engine.CreatePartyGeneralAccount(context.Background(), moneyParty, testMarketAsset) 65 marginMoneyParty, err := eng.Engine.CreatePartyMarginAccount(context.Background(), moneyParty, testMarketID, testMarketAsset) 66 assert.Nil(t, err) 67 68 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 69 err = eng.Engine.UpdateBalance(context.Background(), marginMoneyParty, num.UintZero().Mul(num.NewUint(5), price)) 70 assert.Nil(t, err) 71 72 pos := []*types.Transfer{ 73 { 74 Owner: types.NetworkParty, 75 Amount: &types.FinancialAmount{ 76 Amount: price, 77 Asset: testMarketAsset, 78 }, 79 Type: types.TransferTypeMTMLoss, 80 }, 81 { 82 Owner: moneyParty, 83 Amount: &types.FinancialAmount{ 84 Amount: price, 85 Asset: testMarketAsset, 86 }, 87 Type: types.TransferTypeMTMLoss, 88 }, 89 { 90 Owner: party, 91 Amount: &types.FinancialAmount{ 92 Amount: num.Sum(price, price), // one winning party 93 Asset: testMarketAsset, 94 }, 95 Type: types.TransferTypeMTMWin, 96 }, 97 } 98 99 eng.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes().Do(func(evts []events.Event) { 100 for _, e := range evts { 101 if lse, ok := e.(PartyEvt); ok { 102 require.NotEqual(t, types.NetworkParty, lse.Party()) 103 } 104 } 105 }) 106 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes().Do(func(evt events.Event) { 107 ae, ok := evt.(accEvt) 108 assert.True(t, ok) 109 acc := ae.Account() 110 // we should never receive an event where an account is owned by the network 111 require.False(t, acc.Owner == types.NetworkParty) 112 if acc.Owner == party && acc.Type == types.AccountTypeGeneral { 113 assert.Equal(t, acc.Balance, int64(833)) 114 } 115 if acc.Owner == moneyParty && acc.Type == types.AccountTypeGeneral { 116 assert.Equal(t, acc.Balance, int64(1666)) 117 } 118 }) 119 transfers := eng.getTestMTMTransfer(pos) 120 evts, raw, err := eng.MarkToMarket(context.Background(), testMarketID, transfers, "BTC", func(string) bool { return true }) 121 assert.NoError(t, err) 122 assert.Equal(t, 3, len(raw)) 123 assert.NotEmpty(t, evts) 124 for _, e := range evts { 125 require.NotEqual(t, types.NetworkParty, e.Party()) // there should be no margin events for the network 126 } 127 found := false // we should see a transfer from insurance to settlement 128 for _, r := range raw { 129 for _, tr := range r.Entries { 130 if eng.ADtoID(tr.FromAccount) == insurancePool.ID { 131 to, _ := eng.GetAccountByID(eng.ADtoID(tr.ToAccount)) 132 require.Equal(t, types.AccountTypeSettlement, to.Type) 133 require.True(t, tr.Amount.EQ(price)) 134 found = true 135 break 136 } 137 } 138 } 139 require.True(t, found) 140 } 141 142 func testMTMWithNetworkLossSoc(t *testing.T) { 143 party := "test-party" 144 moneyParty := "money-party" 145 price := num.NewUint(1000) 146 147 eng := getTestEngine(t) 148 defer eng.Finish() 149 150 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 151 insurancePool, err := eng.GetMarketInsurancePoolAccount(testMarketID, testMarketAsset) 152 assert.Nil(t, err) 153 err = eng.UpdateBalance(context.Background(), insurancePool.ID, num.UintZero().Div(price, num.NewUint(2))) 154 assert.Nil(t, err) 155 156 // create party accounts 157 eng.broker.EXPECT().Send(gomock.Any()).Times(8) 158 gID, _ := eng.Engine.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 159 mID, err := eng.Engine.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 160 assert.Nil(t, err) 161 162 assert.NotEmpty(t, mID) 163 assert.NotEmpty(t, gID) 164 165 // create + add balance 166 _, _ = eng.Engine.CreatePartyGeneralAccount(context.Background(), moneyParty, testMarketAsset) 167 marginMoneyParty, err := eng.Engine.CreatePartyMarginAccount(context.Background(), moneyParty, testMarketID, testMarketAsset) 168 assert.Nil(t, err) 169 170 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 171 err = eng.Engine.UpdateBalance(context.Background(), marginMoneyParty, num.UintZero().Mul(num.NewUint(5), price)) 172 assert.Nil(t, err) 173 174 pos := []*types.Transfer{ 175 { 176 Owner: types.NetworkParty, 177 Amount: &types.FinancialAmount{ 178 Amount: price, 179 Asset: testMarketAsset, 180 }, 181 Type: types.TransferTypeMTMLoss, 182 }, 183 { 184 Owner: moneyParty, 185 Amount: &types.FinancialAmount{ 186 Amount: price, 187 Asset: testMarketAsset, 188 }, 189 Type: types.TransferTypeMTMLoss, 190 }, 191 { 192 Owner: party, 193 Amount: &types.FinancialAmount{ 194 Amount: num.Sum(price, price), // one winning party 195 Asset: testMarketAsset, 196 }, 197 Type: types.TransferTypeMTMWin, 198 }, 199 } 200 201 eng.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes().Do(func(evts []events.Event) { 202 for _, e := range evts { 203 if lse, ok := e.(PartyEvt); ok { 204 require.NotEqual(t, types.NetworkParty, lse.Party()) 205 } 206 } 207 }) 208 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes().Do(func(evt events.Event) { 209 ae, ok := evt.(accEvt) 210 assert.True(t, ok) 211 acc := ae.Account() 212 // we should never receive an event where an account is owned by the network 213 require.False(t, acc.Owner == types.NetworkParty) 214 if acc.Owner == party && acc.Type == types.AccountTypeGeneral { 215 assert.Equal(t, acc.Balance, int64(833)) 216 } 217 if acc.Owner == moneyParty && acc.Type == types.AccountTypeGeneral { 218 assert.Equal(t, acc.Balance, int64(1666)) 219 } 220 }) 221 transfers := eng.getTestMTMTransfer(pos) 222 evts, raw, err := eng.MarkToMarket(context.Background(), testMarketID, transfers, "BTC", func(string) bool { return true }) 223 assert.NoError(t, err) 224 assert.Equal(t, 3, len(raw)) 225 assert.NotEmpty(t, evts) 226 for _, e := range evts { 227 require.NotEqual(t, types.NetworkParty, e.Party()) // there should be no margin events for the network 228 } 229 found := false // we should see a transfer from insurance to settlement 230 for _, r := range raw { 231 for _, tr := range r.Entries { 232 if eng.ADtoID(tr.FromAccount) == insurancePool.ID { 233 to, _ := eng.GetAccountByID(eng.ADtoID(tr.ToAccount)) 234 require.Equal(t, types.AccountTypeSettlement, to.Type) 235 found = true 236 require.False(t, tr.Amount.EQ(price)) // there wasn't enough balance to pay the full MTM share 237 require.True(t, tr.Amount.EQ(num.UintZero().Div(price, num.NewUint(2)))) 238 break 239 } 240 } 241 } 242 require.True(t, found) 243 }