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 }