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  }