code.vegaprotocol.io/vega@v0.79.0/core/collateral/checkpoint_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  	bmocks "code.vegaprotocol.io/vega/core/broker/mocks"
    23  	"code.vegaprotocol.io/vega/core/collateral"
    24  	"code.vegaprotocol.io/vega/core/collateral/mocks"
    25  	"code.vegaprotocol.io/vega/core/events"
    26  	"code.vegaprotocol.io/vega/core/types"
    27  	"code.vegaprotocol.io/vega/libs/config/encoding"
    28  	"code.vegaprotocol.io/vega/libs/num"
    29  	"code.vegaprotocol.io/vega/libs/proto"
    30  	"code.vegaprotocol.io/vega/logging"
    31  	checkpoint "code.vegaprotocol.io/vega/protos/vega/checkpoint/v1"
    32  
    33  	"github.com/golang/mock/gomock"
    34  	"github.com/stretchr/testify/assert"
    35  	"github.com/stretchr/testify/require"
    36  )
    37  
    38  type checkpointTestEngine struct {
    39  	*collateral.Engine
    40  	ctrl   *gomock.Controller
    41  	broker *bmocks.MockBroker
    42  }
    43  
    44  func newCheckpointTestEngine(t *testing.T) *checkpointTestEngine {
    45  	t.Helper()
    46  	ctrl := gomock.NewController(t)
    47  	timeSvc := mocks.NewMockTimeService(ctrl)
    48  	timeSvc.EXPECT().GetTimeNow().AnyTimes()
    49  
    50  	broker := bmocks.NewMockBroker(ctrl)
    51  	conf := collateral.NewDefaultConfig()
    52  	conf.Level = encoding.LogLevel{Level: logging.DebugLevel}
    53  
    54  	broker.EXPECT().Send(gomock.Any()).Times(7)
    55  
    56  	e := collateral.New(logging.NewTestLogger(), conf, timeSvc, broker)
    57  	e.EnableAsset(context.Background(), types.Asset{
    58  		ID: "VEGA",
    59  		Details: &types.AssetDetails{
    60  			Name:     "VEGA",
    61  			Symbol:   "VEGA",
    62  			Decimals: 5,
    63  			Quantum:  num.DecimalZero(),
    64  			Source: &types.AssetDetailsBuiltinAsset{
    65  				BuiltinAsset: &types.BuiltinAsset{
    66  					MaxFaucetAmountMint: num.UintZero(),
    67  				},
    68  			},
    69  		},
    70  	})
    71  
    72  	return &checkpointTestEngine{
    73  		Engine: e,
    74  		ctrl:   ctrl,
    75  		broker: broker,
    76  	}
    77  }
    78  
    79  func TestCheckPointLoadingWithAlias(t *testing.T) {
    80  	e := newCheckpointTestEngine(t)
    81  
    82  	e.broker.EXPECT().Send(gomock.Any()).Times(7).Do(func(e events.Event) {
    83  		ledgerMovmenentsE, ok := e.(*events.LedgerMovements)
    84  		if !ok {
    85  			return
    86  		}
    87  
    88  		mvts := ledgerMovmenentsE.LedgerMovements()
    89  		assert.Len(t, mvts, 4)
    90  		assert.Len(t, mvts[0].Entries, 1)
    91  		// no owner + from externa
    92  		assert.Nil(t, mvts[0].Entries[0].FromAccount.Owner)
    93  		assert.Equal(t, mvts[0].Entries[0].FromAccount.Type, types.AccountTypeExternal)
    94  		assert.Equal(t, mvts[0].Entries[0].Amount, "1000")
    95  		// to no owner + to reward
    96  		assert.Nil(t, mvts[0].Entries[0].ToAccount.Owner)
    97  		assert.Equal(t, mvts[0].Entries[0].ToAccount.Type, types.AccountTypeNetworkTreasury)
    98  
    99  		// second transfer
   100  		assert.Len(t, mvts[1].Entries, 1)
   101  		// no owner + from external
   102  		assert.Nil(t, mvts[1].Entries[0].FromAccount.Owner)
   103  		assert.Equal(t, mvts[1].Entries[0].FromAccount.Type, types.AccountTypeExternal)
   104  		assert.Equal(t, mvts[1].Entries[0].Amount, "2000")
   105  		// to no owner + to reward
   106  		assert.Nil(t, mvts[1].Entries[0].ToAccount.Owner)
   107  		assert.Equal(t, mvts[1].Entries[0].ToAccount.Type, types.AccountTypeNetworkTreasury)
   108  
   109  		// third transfer
   110  		assert.Len(t, mvts[2].Entries, 1)
   111  		// no owner + from external
   112  		assert.Nil(t, mvts[2].Entries[0].FromAccount.Owner)
   113  		assert.Equal(t, mvts[2].Entries[0].FromAccount.Type, types.AccountTypeExternal)
   114  		assert.Equal(t, mvts[2].Entries[0].Amount, "9000")
   115  		// to no owner + to global insurnace
   116  		assert.Nil(t, mvts[2].Entries[0].ToAccount.Owner)
   117  		assert.Equal(t, mvts[2].Entries[0].ToAccount.Type, types.AccountTypeGlobalInsurance)
   118  	})
   119  
   120  	ab := []*checkpoint.AssetBalance{
   121  		{Party: "*", Asset: "VEGA", Balance: "1000"},
   122  		{Party: "*ACCOUNT_TYPE_NETWORK_TREASURY", Asset: "VEGA", Balance: "2000"},
   123  		{Party: "*ACCOUNT_TYPE_GLOBAL_INSURANCE", Asset: "VEGA", Balance: "9000"},
   124  		// covers for vesting accounts
   125  		{Party: "vesting6d449ee7716fc5c740b2fe7596ceb91d671ec6f7b9d771edf4a610829bb8a658", Asset: "VEGA", Balance: "4242424"},
   126  	}
   127  
   128  	msg := &checkpoint.Collateral{
   129  		Balances: ab,
   130  	}
   131  
   132  	ret, err := proto.Marshal(msg)
   133  	require.NoError(t, err)
   134  
   135  	e.Load(context.Background(), ret)
   136  
   137  	acc, err := e.GetNetworkTreasuryAccount("VEGA")
   138  	require.NoError(t, err)
   139  	require.Equal(t, "3000", acc.Balance.String())
   140  
   141  	acc, err = e.GetGlobalInsuranceAccount("VEGA")
   142  	require.NoError(t, err)
   143  	require.Equal(t, "9000", acc.Balance.String())
   144  
   145  	acc = e.GetOrCreatePartyVestingRewardAccount(
   146  		context.Background(),
   147  		"6d449ee7716fc5c740b2fe7596ceb91d671ec6f7b9d771edf4a610829bb8a658",
   148  		"VEGA",
   149  	)
   150  
   151  	require.Equal(t, "4242424", acc.Balance.String())
   152  
   153  	_, err = e.GetPartyGeneralAccount("*ACCOUNT_TYPE_GLOBAL_REWARD", "VEGA")
   154  	require.Error(t, err)
   155  }
   156  
   157  type feesTransfer struct {
   158  	totalFeesAmountsPerParty map[string]*num.Uint
   159  	transfers                []*types.Transfer
   160  }
   161  
   162  func (f *feesTransfer) TotalFeesAmountPerParty() map[string]*num.Uint {
   163  	ret := make(map[string]*num.Uint, len(f.totalFeesAmountsPerParty))
   164  	for k, v := range f.totalFeesAmountsPerParty {
   165  		ret[k] = v.Clone()
   166  	}
   167  	return ret
   168  }
   169  func (f *feesTransfer) Transfers() []*types.Transfer { return f.transfers }
   170  
   171  // TestCheckPointWithUndistributedLPFees takes a checkpoint with undistributed balance in the lp fees account of a market and verifies that it goes
   172  // back to the network treasury of the asset as takes a checkpoint.
   173  func TestCheckPointWithUndistributedLPFees(t *testing.T) {
   174  	e := newCheckpointTestEngine(t)
   175  
   176  	e.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   177  
   178  	asset1 := types.Asset{
   179  		ID: "MYASSET1",
   180  		Details: &types.AssetDetails{
   181  			Symbol: "MYASSET1",
   182  		},
   183  	}
   184  	err := e.EnableAsset(context.Background(), asset1)
   185  	require.NoError(t, err)
   186  
   187  	asset2 := types.Asset{
   188  		ID: "MYASSET2",
   189  		Details: &types.AssetDetails{
   190  			Symbol: "MYASSET2",
   191  		},
   192  	}
   193  	err = e.EnableAsset(context.Background(), asset2)
   194  	e.EnableAsset(context.Background(), asset2)
   195  	require.NoError(t, err)
   196  
   197  	// create necessary accounts
   198  	_, _, err = e.CreateMarketAccounts(context.Background(), "market1", "MYASSET1")
   199  	require.NoError(t, err)
   200  
   201  	_, _, err = e.CreateMarketAccounts(context.Background(), "market2", "MYASSET1")
   202  	require.NoError(t, err)
   203  
   204  	_, _, err = e.CreateMarketAccounts(context.Background(), "market3", "MYASSET2")
   205  	require.NoError(t, err)
   206  
   207  	_, err = e.CreatePartyGeneralAccount(context.Background(), "zohar", "MYASSET1")
   208  	require.NoError(t, err)
   209  
   210  	_, err = e.CreatePartyGeneralAccount(context.Background(), "zohar", "MYASSET2")
   211  	require.NoError(t, err)
   212  
   213  	marginAccount1, err := e.CreatePartyMarginAccount(context.Background(), "zohar", "market1", "MYASSET1")
   214  	require.NoError(t, err)
   215  	e.IncrementBalance(context.Background(), marginAccount1, num.NewUint(500000))
   216  
   217  	marginAccount2, err := e.CreatePartyMarginAccount(context.Background(), "zohar", "market2", "MYASSET1")
   218  	require.NoError(t, err)
   219  	e.IncrementBalance(context.Background(), marginAccount2, num.NewUint(500000))
   220  
   221  	marginAccount3, err := e.CreatePartyMarginAccount(context.Background(), "zohar", "market3", "MYASSET2")
   222  	require.NoError(t, err)
   223  	e.IncrementBalance(context.Background(), marginAccount3, num.NewUint(500000))
   224  
   225  	_, err = e.GetOrCreateLiquidityFeesBonusDistributionAccount(context.Background(), "market1", "MYASSET1")
   226  	require.NoError(t, err)
   227  
   228  	partyLiquidityFeeAccountID, err := e.CreatePartyLiquidityFeeAccount(context.Background(), "zohar", "market1", "MYASSET1")
   229  	require.NoError(t, err)
   230  
   231  	e.IncrementBalance(context.Background(), partyLiquidityFeeAccountID, num.NewUint(1234))
   232  
   233  	// setup some balance on the LP fee pay account for MYASSET1/market1
   234  	lpTransfers := &types.Transfer{
   235  		Owner: "zohar",
   236  		Amount: &types.FinancialAmount{
   237  			Asset:  "MYASSET1",
   238  			Amount: num.NewUint(2000),
   239  		},
   240  		Type: types.TransferTypeLiquidityFeePay,
   241  	}
   242  	_, err = e.TransferFees(context.Background(), "market1", "MYASSET1", &feesTransfer{transfers: []*types.Transfer{lpTransfers}})
   243  	require.NoError(t, err)
   244  
   245  	// artificially fill the LP fee account for spots to demonstrate that the unpaid collected goes to the network treasury and what's left
   246  	// on the party LP fee account goes to the general party account
   247  	lpSpotTransfers := &types.Transfer{
   248  		Owner: "zohar",
   249  		Amount: &types.FinancialAmount{
   250  			Asset:  "MYASSET1",
   251  			Amount: num.NewUint(1230),
   252  		},
   253  		Type: types.TransferTypeLiquidityFeeUnpaidCollect,
   254  	}
   255  	_, err = e.TransferSpotFees(context.Background(), "market1", "MYASSET1", &feesTransfer{transfers: []*types.Transfer{lpSpotTransfers}}, types.AccountTypeGeneral)
   256  	require.NoError(t, err)
   257  
   258  	// setup some balance on the LP fee pay account for MYASSET1/market2
   259  	lpTransfers = &types.Transfer{
   260  		Owner: "zohar",
   261  		Amount: &types.FinancialAmount{
   262  			Asset:  "MYASSET1",
   263  			Amount: num.NewUint(3000),
   264  		},
   265  		Type: types.TransferTypeLiquidityFeePay,
   266  	}
   267  	_, err = e.TransferFees(context.Background(), "market2", "MYASSET1", &feesTransfer{transfers: []*types.Transfer{lpTransfers}})
   268  	require.NoError(t, err)
   269  
   270  	// setup some balance on the LP fee pay account for MYASSET1/market1
   271  	lpTransfers = &types.Transfer{
   272  		Owner: "zohar",
   273  		Amount: &types.FinancialAmount{
   274  			Asset:  "MYASSET2",
   275  			Amount: num.NewUint(7000),
   276  		},
   277  		Type: types.TransferTypeLiquidityFeePay,
   278  	}
   279  	_, err = e.TransferFees(context.Background(), "market3", "MYASSET2", &feesTransfer{transfers: []*types.Transfer{lpTransfers}})
   280  	require.NoError(t, err)
   281  
   282  	// take a checkpoint, at this point we expect the funds to be dropped into the network treasury of the asset2.
   283  	ret, err := e.Checkpoint()
   284  	require.NoError(t, err)
   285  
   286  	e.Load(context.Background(), ret)
   287  
   288  	netTreasury1, err := e.GetNetworkTreasuryAccount("MYASSET1")
   289  	require.NoError(t, err)
   290  	require.Equal(t, "6230", netTreasury1.Balance.String())
   291  
   292  	netTreasury2, err := e.GetNetworkTreasuryAccount("MYASSET2")
   293  	require.NoError(t, err)
   294  	require.Equal(t, "7000", netTreasury2.Balance.String())
   295  
   296  	// 1000000 - 5000 + 4
   297  	acc, err := e.GetPartyGeneralAccount("zohar", "MYASSET1")
   298  	require.NoError(t, err)
   299  	require.Equal(t, "995004", acc.Balance.String())
   300  }