code.vegaprotocol.io/vega@v0.79.0/core/collateral/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 collateral_test 17 18 import ( 19 "context" 20 "encoding/hex" 21 "fmt" 22 "strconv" 23 "testing" 24 "time" 25 26 bmocks "code.vegaprotocol.io/vega/core/broker/mocks" 27 "code.vegaprotocol.io/vega/core/collateral" 28 "code.vegaprotocol.io/vega/core/collateral/mocks" 29 "code.vegaprotocol.io/vega/core/events" 30 "code.vegaprotocol.io/vega/core/types" 31 "code.vegaprotocol.io/vega/libs/config/encoding" 32 "code.vegaprotocol.io/vega/libs/crypto" 33 "code.vegaprotocol.io/vega/libs/num" 34 "code.vegaprotocol.io/vega/libs/proto" 35 "code.vegaprotocol.io/vega/logging" 36 ptypes "code.vegaprotocol.io/vega/protos/vega" 37 38 "github.com/golang/mock/gomock" 39 "github.com/pkg/errors" 40 "github.com/stretchr/testify/assert" 41 "github.com/stretchr/testify/require" 42 ) 43 44 const ( 45 testMarketID = "7CPSHJB35AIQBTNMIE6NLFPZGHOYRQ3D" 46 testMarketAsset = "BTC" 47 rewardsID = "0000000000000000000000000000000000000000000000000000000000000000" 48 ) 49 50 type testEngine struct { 51 *collateral.Engine 52 ctrl *gomock.Controller 53 timeSvc *mocks.MockTimeService 54 broker *bmocks.MockBroker 55 systemAccs []*types.Account 56 marketInsuranceID string 57 marketSettlementID string 58 } 59 60 type accEvt interface { 61 events.Event 62 Account() ptypes.Account 63 } 64 65 func TestCollateralTransfer(t *testing.T) { 66 t.Run("test creating new - should create market accounts", testNew) 67 t.Run("test collecting buys - both insurance and sufficient in party accounts", testTransferLoss) 68 t.Run("test collecting buys - party account not empty, but insufficient", testTransferComplexLoss) 69 t.Run("test collecting buys - party missing some accounts", testTransferLossMissingPartyAccounts) 70 t.Run("test collecting both buys and sells - Successfully collect buy and sell in a single call", testProcessBoth) 71 t.Run("test distribution insufficient funds - Transfer losses (partial), distribute wins pro-rate", testProcessBothProRated) 72 t.Run("test releas party margin account", testReleasePartyMarginAccount) 73 } 74 75 func TestCollateralMarkToMarket(t *testing.T) { 76 t.Run("Mark to Market distribution, insufficient funds - complex scenario", testProcessBothProRatedMTM) 77 t.Run("Mark to Market successful", testMTMSuccess) 78 t.Run("Perp funding settlement - successful", testPerpFundingSuccess) 79 t.Run("Perp funding settlement with round - successful", testPerpFundingSuccessWithRound) 80 // we panic if settlement account is non-zero, this test doesn't pass anymore 81 t.Run("Mark to Market wins and losses do not match up, settlement not drained", testSettleBalanceNotZero) 82 } 83 84 func TestAddPartyToMarket(t *testing.T) { 85 t.Run("Successful calls adding new parties (one duplicate, one actual new)", testAddParty) 86 t.Run("Can add a party margin account if general account for asset exists", testAddMarginAccount) 87 t.Run("Fail add party margin account if no general account for asset exisrts", testAddMarginAccountFail) 88 } 89 90 func TestRemoveDistressed(t *testing.T) { 91 t.Run("Successfully remove distressed party and transfer balance", testRemoveDistressedBalance) 92 t.Run("Successfully remove distressed party, no balance transfer", testRemoveDistressedNoBalance) 93 } 94 95 func TestMarginUpdateOnOrder(t *testing.T) { 96 t.Run("Successfully update margin on new order if general account balance is OK", testMarginUpdateOnOrderOK) 97 t.Run("Successfully update margin on new order if general account balance is OK no shortfall with bond accound", testMarginUpdateOnOrderOKNotShortFallWithBondAccount) 98 t.Run("Successfully update margin on new order if general account balance is OK will use bond account if exists", testMarginUpdateOnOrderOKUseBondAccount) 99 t.Run("Successfully update margin on new order if general account balance is OK will use bond&general accounts if exists", testMarginUpdateOnOrderOKUseBondAndGeneralAccounts) 100 t.Run("Successfully update margin on new order then rollback", testMarginUpdateOnOrderOKThenRollback) 101 t.Run("Failed to update margin on new order if general account balance is OK", testMarginUpdateOnOrderFail) 102 } 103 104 func TestEnableAssets(t *testing.T) { 105 t.Run("enable new asset - success", testEnableAssetSuccess) 106 t.Run("enable new asset - failure duplicate", testEnableAssetFailureDuplicate) 107 t.Run("create new account for bad asset - failure", testCreateNewAccountForBadAsset) 108 } 109 110 func TestBalanceTracking(t *testing.T) { 111 t.Run("test a party with an account has a balance", testPartyWithAccountHasABalance) 112 } 113 114 func TestCollateralContinuousTradingFeeTransfer(t *testing.T) { 115 t.Run("Fees transfer continuous - no transfer", testFeesTransferContinuousNoTransfer) 116 t.Run("fees transfer continuous - not funds", testFeeTransferContinuousNoFunds) 117 t.Run("fees transfer continuous - not enough funds", testFeeTransferContinuousNotEnoughFunds) 118 t.Run("fees transfer continuous - OK with enough in margin", testFeeTransferContinuousOKWithEnoughInMargin) 119 t.Run("fees transfer continuous - OK with enough in general", testFeeTransferContinuousOKWithEnoughInGenral) 120 t.Run("fees transfer continuous - OK with enough in margin + general", testFeeTransferContinuousOKWithEnoughInGeneralAndMargin) 121 t.Run("fees transfer continuous - transfer with 0 amount", testFeeTransferContinuousOKWith0Amount) 122 t.Run("fees transfer check account events", testFeeTransferContinuousOKCheckAccountEvents) 123 } 124 125 func TestCreateBondAccount(t *testing.T) { 126 t.Run("create a bond account with success", testCreateBondAccountSuccess) 127 t.Run("create a bond account with - failure no general account", testCreateBondAccountFailureNoGeneral) 128 } 129 130 func TestTransferRewards(t *testing.T) { 131 t.Run("transfer rewards empty slice", testTransferRewardsEmptySlice) 132 t.Run("transfer rewards missing rewards account", testTransferRewardsNoRewardsAccount) 133 t.Run("transfer rewards success", testTransferRewardsSuccess) 134 } 135 136 func TestClearAccounts(t *testing.T) { 137 t.Run("clear fee accounts", testClearFeeAccounts) 138 } 139 140 func TestGetAllVestingQuantumBalance(t *testing.T) { 141 eng := getTestEngine(t) 142 defer eng.Finish() 143 ctx := context.Background() 144 145 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 146 147 // try with no assets. 148 // unlikely this would happenm but eh 149 party := "party1" 150 151 balance := eng.GetAllVestingQuantumBalance(party) 152 assert.Equal(t, balance.String(), "0") 153 154 assetT := types.Asset{ 155 ID: "USDC", 156 Details: &types.AssetDetails{ 157 Symbol: "USDC", 158 Quantum: num.MustDecimalFromString("100"), 159 }, 160 } 161 162 eng.EnableAsset(ctx, assetT) 163 164 assetT = types.Asset{ 165 ID: "VEGA", 166 Details: &types.AssetDetails{ 167 Symbol: "VEGA", 168 Quantum: num.MustDecimalFromString("300"), 169 }, 170 } 171 172 eng.EnableAsset(ctx, assetT) 173 174 // now add some balance to an asset 175 acc := eng.GetOrCreatePartyVestingRewardAccount(ctx, party, "USDC") 176 // in quantum == 100 177 assert.NoError( 178 t, eng.UpdateBalance(ctx, acc.ID, num.NewUint(10000)), 179 ) 180 181 balance = eng.GetAllVestingQuantumBalance(party) 182 assert.Equal(t, balance.String(), "100") 183 184 // add some more of the other account 185 // now add some balance to an asset 186 acc = eng.GetOrCreatePartyVestedRewardAccount(ctx, party, "VEGA") 187 // in quantum == 3.5, integer partused only 188 assert.NoError( 189 t, eng.UpdateBalance(ctx, acc.ID, num.NewUint(950)), 190 ) 191 192 balance = eng.GetAllVestingQuantumBalance(party) 193 assert.Equal(t, balance.String(), "103.1666666666666667") 194 } 195 196 func testClearFeeAccounts(t *testing.T) { 197 eng := getTestEngine(t) 198 defer eng.Finish() 199 ctx := context.Background() 200 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 201 mktID := "market" 202 asset := "ETH" 203 party := "myparty" 204 assetT := types.Asset{ 205 ID: asset, 206 Details: &types.AssetDetails{ 207 Symbol: asset, 208 }, 209 } 210 211 eng.EnableAsset(ctx, assetT) 212 _, _ = eng.GetGlobalRewardAccount(asset) 213 _, _, err := eng.CreateMarketAccounts(ctx, mktID, asset) 214 require.NoError(t, err) 215 general, err := eng.CreatePartyGeneralAccount(ctx, party, asset) 216 require.NoError(t, err) 217 218 _, err = eng.CreatePartyMarginAccount(ctx, party, mktID, asset) 219 require.NoError(t, err) 220 221 // add funds 222 err = eng.UpdateBalance(ctx, general, num.NewUint(10000)) 223 assert.Nil(t, err) 224 225 transferFeesReq := transferFees{ 226 tfs: []*types.Transfer{ 227 { 228 Owner: party, 229 Amount: &types.FinancialAmount{ 230 Amount: num.NewUint(1000), 231 }, 232 Type: types.TransferTypeMakerFeePay, 233 MinAmount: num.NewUint(1000), 234 }, 235 }, 236 tfa: map[string]uint64{party: 1000}, 237 } 238 239 transfers, err := eng.TransferFeesContinuousTrading(ctx, mktID, asset, transferFeesReq) 240 assert.NotNil(t, transfers) 241 assert.NoError(t, err, collateral.ErrInsufficientFundsToPayFees.Error()) 242 assert.Len(t, transfers, 1) 243 244 assert.Equal(t, 1, len(transfers[0].Entries)) 245 assert.Equal(t, num.NewUint(9000), transfers[0].Entries[0].FromAccountBalance) 246 assert.Equal(t, num.NewUint(1000), transfers[0].Entries[0].ToAccountBalance) 247 eng.ClearInsurancepool(ctx, mktID, asset, true) 248 } 249 250 func testTransferRewardsEmptySlice(t *testing.T) { 251 eng := getTestEngine(t) 252 defer eng.Finish() 253 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 254 res, err := eng.TransferRewards(context.Background(), "reward", []*types.Transfer{}, types.AccountTypeGlobalReward) 255 assert.Nil(t, err) 256 assert.Equal(t, 0, len(res)) 257 } 258 259 func testTransferRewardsNoRewardsAccount(t *testing.T) { 260 eng := getTestEngine(t) 261 defer eng.Finish() 262 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 263 264 transfers := []*types.Transfer{ 265 { 266 Amount: &types.FinancialAmount{ 267 Amount: num.NewUint(1000), 268 Asset: "ETH", 269 }, 270 MinAmount: num.NewUint(1000), 271 Type: types.TransferTypeRewardPayout, 272 Owner: "party1", 273 }, 274 } 275 276 res, err := eng.TransferRewards(context.Background(), "rewardAccID", transfers, types.AccountTypeGlobalReward) 277 require.Error(t, errors.New("account does not exists"), err) 278 require.Nil(t, res) 279 } 280 281 func testTransferRewardsSuccess(t *testing.T) { 282 eng := getTestEngine(t) 283 defer eng.Finish() 284 285 rewardAcc, _ := eng.GetGlobalRewardAccount("ETH") 286 287 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 288 eng.IncrementBalance(context.Background(), rewardAcc.ID, num.NewUint(1000)) 289 290 partyAccountID := eng.GetOrCreatePartyVestingRewardAccount(context.Background(), "party1", "ETH").ID 291 292 transfers := []*types.Transfer{ 293 { 294 Owner: "party1", 295 Amount: &types.FinancialAmount{ 296 Amount: num.NewUint(1000), 297 Asset: "ETH", 298 }, 299 MinAmount: num.NewUint(1000), 300 Type: types.TransferTypeRewardPayout, 301 }, 302 } 303 304 lm, err := eng.TransferRewards(context.Background(), rewardAcc.ID, transfers, types.AccountTypeGlobalReward) 305 require.Nil(t, err) 306 partyAccount, _ := eng.GetAccountByID(partyAccountID) 307 require.Equal(t, num.NewUint(1000), partyAccount.Balance) 308 309 rewardAccount, _ := eng.GetGlobalRewardAccount("ETH") 310 require.Equal(t, num.UintZero(), rewardAccount.Balance) 311 312 assert.Equal(t, 1, len(lm)) 313 assert.Equal(t, 1, len(lm[0].Entries)) 314 assert.Equal(t, num.NewUint(1000), lm[0].Entries[0].ToAccountBalance) 315 assert.Equal(t, num.UintZero(), lm[0].Entries[0].FromAccountBalance) 316 } 317 318 func testPartyWithAccountHasABalance(t *testing.T) { 319 eng := getTestEngine(t) 320 defer eng.Finish() 321 322 party := "myparty" 323 bal := num.NewUint(500) 324 // create party 325 eng.broker.EXPECT().Send(gomock.Any()).Times(3) 326 acc, err := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 327 assert.NoError(t, err) 328 329 // then add some money 330 err = eng.UpdateBalance(context.Background(), acc, bal) 331 assert.Nil(t, err) 332 333 evt := eng.broker.GetLastByTypeAndID(events.AccountEvent, acc) 334 require.NotNil(t, evt) 335 _, ok := evt.(accEvt) 336 require.True(t, ok) 337 } 338 339 func testCreateBondAccountFailureNoGeneral(t *testing.T) { 340 eng := getTestEngine(t) 341 defer eng.Finish() 342 343 party := "myparty" 344 // create party 345 _, err := eng.CreatePartyBondAccount(context.Background(), party, testMarketID, testMarketAsset) 346 assert.EqualError(t, err, "party general account missing when trying to create a bond account") 347 } 348 349 func testCreateBondAccountSuccess(t *testing.T) { 350 eng := getTestEngine(t) 351 defer eng.Finish() 352 353 party := "myparty" 354 bal := num.NewUint(500) 355 // create party 356 eng.broker.EXPECT().Send(gomock.Any()).Times(3) 357 _, err := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 358 require.NoError(t, err) 359 bnd, err := eng.CreatePartyBondAccount(context.Background(), party, testMarketID, testMarketAsset) 360 require.NoError(t, err) 361 362 // add funds 363 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 364 err = eng.UpdateBalance(context.Background(), bnd, bal) 365 assert.Nil(t, err) 366 367 evt := eng.broker.GetLastByTypeAndID(events.AccountEvent, bnd) 368 require.NotNil(t, evt) 369 ae, ok := evt.(accEvt) 370 require.True(t, ok) 371 account := ae.Account() 372 require.Equal(t, bal.String(), account.Balance) 373 // these two checks are a bit redundant at this point 374 // but at least we're verifying that the GetAccountByID and the latest event return the same state 375 bndacc, _ := eng.GetAccountByID(bnd) 376 assert.Equal(t, account.Balance, bndacc.Balance.String()) 377 } 378 379 func TestDeleteBondAccount(t *testing.T) { 380 eng := getTestEngine(t) 381 defer eng.Finish() 382 eng.broker.EXPECT().Send(gomock.Any()).Times(6) 383 384 party := "myparty" 385 err := eng.RemoveBondAccount(party, testMarketID, testMarketAsset) 386 require.EqualError(t, err, collateral.ErrAccountDoesNotExist.Error()) 387 388 bal := num.NewUint(500) 389 // create party 390 _, err = eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 391 require.NoError(t, err) 392 bnd, err := eng.CreatePartyBondAccount(context.Background(), party, testMarketID, testMarketAsset) 393 require.NoError(t, err) 394 395 // add funds 396 err = eng.UpdateBalance(context.Background(), bnd, bal) 397 require.Nil(t, err) 398 399 require.Panics(t, func() { eng.RemoveBondAccount(party, testMarketID, testMarketAsset) }) 400 401 transfer := &types.Transfer{ 402 Owner: party, 403 Amount: &types.FinancialAmount{ 404 Amount: bal, 405 Asset: testMarketAsset, 406 }, 407 Type: types.TransferTypeBondHigh, 408 MinAmount: bal, 409 } 410 411 _, err = eng.BondUpdate(context.Background(), testMarketID, transfer) 412 require.NoError(t, err) 413 414 err = eng.RemoveBondAccount(party, testMarketID, testMarketAsset) 415 require.NoError(t, err) 416 417 _, err = eng.GetPartyBondAccount(testMarketID, party, testMarketAsset) 418 419 require.ErrorContains(t, err, "account does not exist") 420 } 421 422 func testFeesTransferContinuousNoTransfer(t *testing.T) { 423 eng := getTestEngine(t) 424 defer eng.Finish() 425 426 transfers, err := eng.TransferFeesContinuousTrading( 427 context.Background(), testMarketID, testMarketAsset, transferFees{}) 428 assert.Nil(t, transfers) 429 assert.Nil(t, err) 430 } 431 432 func TestPartyHasSufficientBalanceForFees(t *testing.T) { 433 eng := getTestEngine(t) 434 defer eng.Finish() 435 436 party := "myparty" 437 // create party 438 eng.broker.EXPECT().Send(gomock.Any()).Times(3) 439 gen, err := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 440 require.NoError(t, err) 441 442 mar, err := eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 443 require.NoError(t, err) 444 445 // add funds 446 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 447 err = eng.UpdateBalance(context.Background(), gen, num.NewUint(100)) 448 assert.Nil(t, err) 449 450 // there's no margin account balance but the party has enough funds in the margin account 451 require.Nil(t, eng.PartyCanCoverFees(testMarketAsset, testMarketID, party, num.NewUint(50))) 452 453 // there's no margin account balance and the party has insufficient funds to cover fees in the general account 454 require.Error(t, fmt.Errorf("party has insufficient funds to cover fees"), eng.PartyCanCoverFees(testMarketAsset, testMarketID, party, num.NewUint(101))) 455 456 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 457 err = eng.UpdateBalance(context.Background(), mar, num.NewUint(500)) 458 assert.Nil(t, err) 459 460 // there's enough in the margin + general to cover the fees 461 require.Nil(t, eng.PartyCanCoverFees(testMarketAsset, testMarketID, party, num.NewUint(101))) 462 463 // there's not enough in the margin + general to cover the fees 464 require.Error(t, fmt.Errorf("party has insufficient funds to cover fees"), eng.PartyCanCoverFees(testMarketAsset, testMarketID, party, num.NewUint(601))) 465 } 466 467 func testReleasePartyMarginAccount(t *testing.T) { 468 eng := getTestEngine(t) 469 defer eng.Finish() 470 471 party := "myparty" 472 // create party 473 eng.broker.EXPECT().Send(gomock.Any()).Times(3) 474 gen, err := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 475 require.NoError(t, err) 476 477 mar, err := eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 478 require.NoError(t, err) 479 480 // add funds 481 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 482 err = eng.UpdateBalance(context.Background(), gen, num.NewUint(100)) 483 assert.Nil(t, err) 484 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 485 err = eng.UpdateBalance(context.Background(), mar, num.NewUint(500)) 486 assert.Nil(t, err) 487 488 eng.broker.EXPECT().Send(gomock.Any()).Times(2) 489 lm, err := eng.ClearPartyMarginAccount( 490 context.Background(), party, testMarketID, testMarketAsset) 491 assert.NoError(t, err) 492 generalAcc, _ := eng.GetAccountByID(gen) 493 assert.Equal(t, num.NewUint(600), generalAcc.Balance) 494 marginAcc, _ := eng.GetAccountByID(mar) 495 assert.True(t, marginAcc.Balance.IsZero()) 496 497 assert.Equal(t, 1, len(lm.Entries)) 498 assert.Equal(t, num.NewUint(600), lm.Entries[0].ToAccountBalance) 499 assert.Equal(t, num.UintZero(), lm.Entries[0].FromAccountBalance) 500 } 501 502 func testFeeTransferContinuousNoFunds(t *testing.T) { 503 eng := getTestEngine(t) 504 defer eng.Finish() 505 506 party := "myparty" 507 // create party 508 eng.broker.EXPECT().Send(gomock.Any()).Times(3) 509 _, err := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 510 require.NoError(t, err) 511 512 _, err = eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 513 require.NoError(t, err) 514 515 transferFeesReq := transferFees{ 516 tfs: []*types.Transfer{ 517 { 518 Owner: "myparty", 519 Amount: &types.FinancialAmount{ 520 Amount: num.NewUint(1000), 521 }, 522 Type: types.TransferTypeInfrastructureFeePay, 523 MinAmount: num.NewUint(1000), 524 }, 525 }, 526 tfa: map[string]uint64{party: 1000}, 527 } 528 529 transfers, err := eng.TransferFeesContinuousTrading( 530 context.Background(), testMarketID, testMarketAsset, transferFeesReq) 531 assert.Nil(t, transfers) 532 assert.EqualError(t, err, collateral.ErrInsufficientFundsToPayFees.Error()) 533 } 534 535 func testFeeTransferContinuousNotEnoughFunds(t *testing.T) { 536 eng := getTestEngine(t) 537 defer eng.Finish() 538 party := "myparty" 539 // create party 540 eng.broker.EXPECT().Send(gomock.Any()).Times(3) 541 general, err := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 542 require.NoError(t, err) 543 544 _, err = eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 545 require.NoError(t, err) 546 547 // add funds 548 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 549 err = eng.UpdateBalance(context.Background(), general, num.NewUint(100)) 550 assert.Nil(t, err) 551 552 transferFeesReq := transferFees{ 553 tfs: []*types.Transfer{ 554 { 555 Owner: "myparty", 556 Amount: &types.FinancialAmount{ 557 Amount: num.NewUint(1000), 558 }, 559 Type: types.TransferTypeInfrastructureFeePay, 560 MinAmount: num.NewUint(1000), 561 }, 562 }, 563 tfa: map[string]uint64{party: 1000}, 564 } 565 566 transfers, err := eng.TransferFeesContinuousTrading( 567 context.Background(), testMarketID, testMarketAsset, transferFeesReq) 568 assert.Nil(t, transfers) 569 assert.EqualError(t, err, collateral.ErrInsufficientFundsToPayFees.Error()) 570 } 571 572 func testFeeTransferContinuousOKWithEnoughInGenral(t *testing.T) { 573 eng := getTestEngine(t) 574 defer eng.Finish() 575 party := "myparty" 576 // create party 577 eng.broker.EXPECT().Send(gomock.Any()).Times(4) 578 general, err := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 579 require.NoError(t, err) 580 581 _, err = eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 582 require.NoError(t, err) 583 584 // add funds 585 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 586 err = eng.UpdateBalance(context.Background(), general, num.NewUint(10000)) 587 assert.Nil(t, err) 588 589 transferFeesReq := transferFees{ 590 tfs: []*types.Transfer{ 591 { 592 Owner: "myparty", 593 Amount: &types.FinancialAmount{ 594 Amount: num.NewUint(1000), 595 }, 596 Type: types.TransferTypeInfrastructureFeePay, 597 MinAmount: num.NewUint(1000), 598 }, 599 }, 600 tfa: map[string]uint64{party: 1000}, 601 } 602 603 eng.broker.EXPECT().Send(gomock.Any()).Times(2) 604 transfers, err := eng.TransferFeesContinuousTrading( 605 context.Background(), testMarketID, testMarketAsset, transferFeesReq) 606 assert.NotNil(t, transfers) 607 assert.NoError(t, err, collateral.ErrInsufficientFundsToPayFees.Error()) 608 assert.Len(t, transfers, 1) 609 610 assert.Equal(t, 1, len(transfers[0].Entries)) 611 assert.Equal(t, num.NewUint(9000), transfers[0].Entries[0].FromAccountBalance) 612 assert.Equal(t, num.NewUint(1000), transfers[0].Entries[0].ToAccountBalance) 613 } 614 615 func testFeeTransferContinuousOKWith0Amount(t *testing.T) { 616 eng := getTestEngine(t) 617 defer eng.Finish() 618 party := "myparty" 619 // create party 620 eng.broker.EXPECT().Send(gomock.Any()).Times(4) 621 general, err := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 622 require.NoError(t, err) 623 624 _, err = eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 625 require.NoError(t, err) 626 627 // add funds 628 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 629 err = eng.UpdateBalance(context.Background(), general, num.NewUint(10000)) 630 assert.Nil(t, err) 631 632 transferFeesReq := transferFees{ 633 tfs: []*types.Transfer{ 634 { 635 Owner: "myparty", 636 Amount: &types.FinancialAmount{ 637 Amount: num.UintZero(), 638 }, 639 Type: types.TransferTypeInfrastructureFeePay, 640 MinAmount: num.UintZero(), 641 }, 642 }, 643 tfa: map[string]uint64{party: 1000}, 644 } 645 646 eng.broker.EXPECT().Send(gomock.Any()).Times(2) 647 transfers, err := eng.TransferFeesContinuousTrading( 648 context.Background(), testMarketID, testMarketAsset, transferFeesReq) 649 assert.NotNil(t, transfers) 650 assert.NoError(t, err, collateral.ErrInsufficientFundsToPayFees.Error()) 651 assert.Len(t, transfers, 1) 652 generalAcc, _ := eng.GetAccountByID(general) 653 assert.Equal(t, num.NewUint(10000), generalAcc.Balance) 654 655 assert.Len(t, transfers[0].Entries, 1) 656 assert.Equal(t, num.UintZero(), transfers[0].Entries[0].ToAccountBalance) 657 assert.Equal(t, num.NewUint(10000), transfers[0].Entries[0].FromAccountBalance) 658 } 659 660 func testFeeTransferContinuousOKWithEnoughInMargin(t *testing.T) { 661 eng := getTestEngine(t) 662 defer eng.Finish() 663 party := "myparty" 664 // create party 665 eng.broker.EXPECT().Send(gomock.Any()).Times(4) 666 _, err := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 667 require.NoError(t, err) 668 669 margin, err := eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 670 require.NoError(t, err) 671 672 // add funds 673 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 674 err = eng.UpdateBalance(context.Background(), margin, num.NewUint(10000)) 675 assert.Nil(t, err) 676 677 transferFeesReq := transferFees{ 678 tfs: []*types.Transfer{ 679 { 680 Owner: "myparty", 681 Amount: &types.FinancialAmount{ 682 Amount: num.NewUint(1000), 683 }, 684 Type: types.TransferTypeInfrastructureFeePay, 685 MinAmount: num.NewUint(1000), 686 }, 687 }, 688 tfa: map[string]uint64{party: 1000}, 689 } 690 691 eng.broker.EXPECT().Send(gomock.Any()).Times(2) 692 transfers, err := eng.TransferFeesContinuousTrading( 693 context.Background(), testMarketID, testMarketAsset, transferFeesReq) 694 assert.NotNil(t, transfers) 695 assert.NoError(t, err, collateral.ErrInsufficientFundsToPayFees.Error()) 696 assert.Len(t, transfers, 1) 697 assert.Len(t, transfers[0].Entries, 1) 698 assert.Equal(t, num.NewUint(1000), transfers[0].Entries[0].ToAccountBalance) 699 assert.Equal(t, num.NewUint(9000), transfers[0].Entries[0].FromAccountBalance) 700 } 701 702 func testFeeTransferContinuousOKCheckAccountEvents(t *testing.T) { 703 eng := getTestEngine(t) 704 defer eng.Finish() 705 party := "myparty" 706 // create party 707 eng.broker.EXPECT().Send(gomock.Any()).Times(4) 708 _, err := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 709 require.NoError(t, err) 710 711 margin, err := eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 712 require.NoError(t, err) 713 714 // add funds 715 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 716 err = eng.UpdateBalance(context.Background(), margin, num.NewUint(10000)) 717 assert.Nil(t, err) 718 719 transferFeesReq := transferFees{ 720 tfs: []*types.Transfer{ 721 { 722 Owner: "myparty", 723 Amount: &types.FinancialAmount{ 724 Amount: num.NewUint(1000), 725 }, 726 Type: types.TransferTypeInfrastructureFeePay, 727 MinAmount: num.NewUint(1000), 728 }, 729 { 730 Owner: "myparty", 731 Amount: &types.FinancialAmount{ 732 Amount: num.NewUint(3000), 733 }, 734 Type: types.TransferTypeLiquidityFeePay, 735 MinAmount: num.NewUint(3000), 736 }, 737 }, 738 tfa: map[string]uint64{party: 1000}, 739 } 740 741 var ( 742 seenLiqui bool 743 seenInfra bool 744 ) 745 eng.broker.EXPECT().Send(gomock.Any()).Times(4).Do(func(evt events.Event) { 746 if evt.Type() != events.AccountEvent { 747 t.FailNow() 748 } 749 accRaw := evt.(*events.Acc) 750 acc := accRaw.Account() 751 if acc.Type == types.AccountTypeFeesInfrastructure { 752 assert.Equal(t, 1000, stringToInt(acc.Balance)) 753 seenInfra = true 754 } 755 if acc.Type == types.AccountTypeFeesLiquidity { 756 assert.Equal(t, 3000, stringToInt(acc.Balance)) 757 seenLiqui = true 758 } 759 }) 760 transfers, err := eng.TransferFeesContinuousTrading( 761 context.Background(), testMarketID, testMarketAsset, transferFeesReq) 762 assert.NotNil(t, transfers) 763 assert.NoError(t, err, collateral.ErrInsufficientFundsToPayFees.Error()) 764 assert.Len(t, transfers, 2) 765 assert.True(t, seenInfra) 766 assert.True(t, seenLiqui) 767 768 assert.Equal(t, num.NewUint(1000), transfers[0].Entries[0].ToAccountBalance) 769 assert.Equal(t, num.NewUint(9000), transfers[0].Entries[0].FromAccountBalance) 770 assert.Equal(t, num.NewUint(3000), transfers[1].Entries[0].ToAccountBalance) 771 assert.Equal(t, num.NewUint(6000), transfers[1].Entries[0].FromAccountBalance) 772 } 773 774 func testFeeTransferContinuousOKWithEnoughInGeneralAndMargin(t *testing.T) { 775 eng := getTestEngine(t) 776 defer eng.Finish() 777 party := "myparty" 778 // create party 779 eng.broker.EXPECT().Send(gomock.Any()).Times(4) 780 general, err := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 781 require.NoError(t, err) 782 783 margin, err := eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 784 require.NoError(t, err) 785 786 // add funds 787 eng.broker.EXPECT().Send(gomock.Any()).Times(2) 788 err = eng.UpdateBalance(context.Background(), general, num.NewUint(700)) 789 require.NoError(t, err) 790 791 err = eng.UpdateBalance(context.Background(), margin, num.NewUint(900)) 792 require.NoError(t, err) 793 794 transferFeesReq := transferFees{ 795 tfs: []*types.Transfer{ 796 { 797 Owner: "myparty", 798 Amount: &types.FinancialAmount{ 799 Amount: num.NewUint(1000), 800 }, 801 Type: types.TransferTypeInfrastructureFeePay, 802 MinAmount: num.NewUint(1000), 803 }, 804 }, 805 tfa: map[string]uint64{party: 1000}, 806 } 807 808 eng.broker.EXPECT().Send(gomock.Any()).Times(3) 809 transfers, err := eng.TransferFeesContinuousTrading( 810 context.Background(), testMarketID, testMarketAsset, transferFeesReq) 811 assert.NotNil(t, transfers) 812 assert.NoError(t, err, collateral.ErrInsufficientFundsToPayFees.Error()) 813 assert.Len(t, transfers, 1) 814 815 // now check the balances 816 // general should be empty 817 generalAcc, _ := eng.GetAccountByID(general) 818 assert.True(t, generalAcc.Balance.IsZero()) 819 marginAcc, _ := eng.GetAccountByID(margin) 820 assert.Equal(t, num.NewUint(600), marginAcc.Balance) 821 assert.Equal(t, num.NewUint(700), transfers[0].Entries[0].ToAccountBalance) 822 assert.Equal(t, num.UintZero(), transfers[0].Entries[0].FromAccountBalance) 823 } 824 825 func testEnableAssetSuccess(t *testing.T) { 826 eng := getTestEngine(t) 827 defer eng.Finish() 828 asset := types.Asset{ 829 ID: "MYASSET", 830 Details: &types.AssetDetails{ 831 Symbol: "MYASSET", 832 }, 833 } 834 eng.broker.EXPECT().Send(gomock.Any()).Times(7) 835 err := eng.EnableAsset(context.Background(), asset) 836 assert.NoError(t, err) 837 838 assetInsuranceAcc, _ := eng.GetGlobalRewardAccount(asset.ID) 839 assert.True(t, assetInsuranceAcc.Balance.IsZero()) 840 } 841 842 func testEnableAssetFailureDuplicate(t *testing.T) { 843 eng := getTestEngine(t) 844 defer eng.Finish() 845 asset := types.Asset{ 846 ID: "MYASSET", 847 Details: &types.AssetDetails{ 848 Symbol: "MYASSET", 849 }, 850 } 851 eng.broker.EXPECT().Send(gomock.Any()).Times(7) 852 err := eng.EnableAsset(context.Background(), asset) 853 assert.NoError(t, err) 854 855 // now try to enable it again 856 err = eng.EnableAsset(context.Background(), asset) 857 assert.EqualError(t, err, collateral.ErrAssetAlreadyEnabled.Error()) 858 } 859 860 func testCreateNewAccountForBadAsset(t *testing.T) { 861 eng := getTestEngine(t) 862 defer eng.Finish() 863 864 _, err := eng.CreatePartyGeneralAccount(context.Background(), "someparty", "notanasset") 865 assert.EqualError(t, err, collateral.ErrInvalidAssetID.Error()) 866 _, err = eng.CreatePartyMarginAccount(context.Background(), "someparty", testMarketID, "notanasset") 867 assert.EqualError(t, err, collateral.ErrInvalidAssetID.Error()) 868 _, _, err = eng.CreateMarketAccounts(context.Background(), "somemarketid", "notanasset") 869 assert.EqualError(t, err, collateral.ErrInvalidAssetID.Error()) 870 } 871 872 func testNew(t *testing.T) { 873 eng := getTestEngine(t) 874 eng.Finish() 875 } 876 877 func testAddMarginAccount(t *testing.T) { 878 eng := getTestEngine(t) 879 defer eng.Finish() 880 party := "funkyparty" 881 882 eng.broker.EXPECT().Send(gomock.Any()).Times(3) 883 _, _ = eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 884 margin, err := eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 885 assert.Nil(t, err) 886 887 // test balance is 0 when created 888 acc, err := eng.GetAccountByID(margin) 889 assert.Nil(t, err) 890 assert.True(t, acc.Balance.IsZero()) 891 } 892 893 func testAddMarginAccountFail(t *testing.T) { 894 eng := getTestEngine(t) 895 defer eng.Finish() 896 party := "funkyparty" 897 898 // create party 899 _, err := eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 900 assert.Error(t, err, collateral.ErrNoGeneralAccountWhenCreateMarginAccount) 901 } 902 903 func testAddParty(t *testing.T) { 904 eng := getTestEngine(t) 905 defer eng.Finish() 906 party := "funkyparty" 907 908 // create party 909 eng.broker.EXPECT().Send(gomock.Any()).Times(3) 910 general, _ := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 911 margin, err := eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 912 assert.Nil(t, err) 913 914 // add funds 915 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 916 err = eng.UpdateBalance(context.Background(), general, num.NewUint(100000)) 917 assert.Nil(t, err) 918 919 expectedGeneralBalance := num.NewUint(100000) 920 921 // check the amount on each account now 922 acc, err := eng.GetAccountByID(margin) 923 assert.Nil(t, err) 924 assert.True(t, acc.Balance.IsZero()) 925 926 acc, err = eng.GetAccountByID(general) 927 assert.Nil(t, err) 928 assert.Equal(t, expectedGeneralBalance, acc.Balance) 929 } 930 931 func testTransferLoss(t *testing.T) { 932 party := "test-party" 933 moneyParty := "money-party" 934 935 price := num.NewUint(1000) 936 937 eng := getTestEngine(t) 938 defer eng.Finish() 939 940 eng.broker.EXPECT().Send(gomock.Any()).Times(9) 941 942 insurancePool, err := eng.GetMarketInsurancePoolAccount(testMarketID, testMarketAsset) 943 assert.Nil(t, err) 944 err = eng.UpdateBalance(context.Background(), insurancePool.ID, num.UintZero().Mul(price, num.NewUint(5))) 945 assert.Nil(t, err) 946 947 // create party accounts, set balance for money party 948 _, _ = eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 949 _, err = eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 950 assert.Nil(t, err) 951 _, _ = eng.CreatePartyGeneralAccount(context.Background(), moneyParty, testMarketAsset) 952 marginMoneyParty, err := eng.CreatePartyMarginAccount(context.Background(), moneyParty, testMarketID, testMarketAsset) 953 assert.Nil(t, err) 954 955 err = eng.UpdateBalance(context.Background(), marginMoneyParty, num.NewUint(100000)) 956 assert.Nil(t, err) 957 958 // now the positions 959 pos := []*types.Transfer{ 960 { 961 Owner: party, 962 Amount: &types.FinancialAmount{ 963 Amount: price, 964 Asset: "BTC", 965 }, 966 Type: types.TransferTypeLoss, 967 }, 968 { 969 Owner: moneyParty, 970 Amount: &types.FinancialAmount{ 971 Amount: price, 972 Asset: "BTC", 973 }, 974 Type: types.TransferTypeWin, 975 }, 976 } 977 978 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 979 responses, err := eng.FinalSettlement(context.Background(), testMarketID, pos, num.UintOne(), func(string) bool { return true }) 980 assert.NoError(t, err) 981 assert.Equal(t, 2, len(responses)) 982 resp := responses[0] 983 assert.NoError(t, err) 984 // total balance of settlement account should be 2 times price 985 assert.Equal(t, num.Sum(price, price), num.Sum(resp.Balances[0].Balance, responses[1].Balances[0].Balance)) 986 // there should be 1 ledger moves 987 assert.Equal(t, 1, len(resp.Entries)) 988 assert.Equal(t, num.NewUint(4000), resp.Entries[0].FromAccountBalance) 989 assert.Equal(t, num.NewUint(1000), resp.Entries[0].ToAccountBalance) 990 991 assert.Equal(t, 1, len(responses[1].Entries)) 992 assert.Equal(t, num.UintZero(), responses[1].Entries[0].FromAccountBalance) 993 assert.Equal(t, num.NewUint(101000), responses[1].Entries[0].ToAccountBalance) 994 } 995 996 func testTransferComplexLoss(t *testing.T) { 997 party := "test-party" 998 moneyParty := "money-party" 999 half := num.NewUint(500) 1000 price := num.Sum(half, half) 1001 1002 eng := getTestEngine(t) 1003 defer eng.Finish() 1004 1005 eng.broker.EXPECT().Send(gomock.Any()).Times(10) 1006 1007 _, _ = eng.CreatePartyGeneralAccount(context.Background(), moneyParty, testMarketAsset) 1008 marginMoneyParty, err := eng.CreatePartyMarginAccount(context.Background(), moneyParty, testMarketID, testMarketAsset) 1009 assert.Nil(t, err) 1010 1011 err = eng.UpdateBalance(context.Background(), marginMoneyParty, num.NewUint(100000)) 1012 assert.Nil(t, err) 1013 1014 insurancePool, err := eng.GetMarketInsurancePoolAccount(testMarketID, testMarketAsset) 1015 assert.Nil(t, err) 1016 // 5x price 1017 err = eng.UpdateBalance(context.Background(), insurancePool.ID, num.Sum(price, price, price, price, price)) 1018 assert.Nil(t, err) 1019 1020 // create party accounts 1021 eng.broker.EXPECT().Send(gomock.Any()).Times(3) 1022 _, _ = eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 1023 marginParty, err := eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 1024 assert.Nil(t, err) 1025 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 1026 err = eng.IncrementBalance(context.Background(), marginParty, half) 1027 assert.Nil(t, err) 1028 1029 // now the positions 1030 pos := []*types.Transfer{ 1031 { 1032 Owner: party, 1033 Amount: &types.FinancialAmount{ 1034 Asset: "BTC", 1035 Amount: price, 1036 }, 1037 Type: types.TransferTypeLoss, 1038 }, 1039 { 1040 Owner: moneyParty, 1041 Amount: &types.FinancialAmount{ 1042 Asset: "BTC", 1043 Amount: price, 1044 }, 1045 Type: types.TransferTypeWin, 1046 }, 1047 } 1048 1049 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 1050 responses, err := eng.FinalSettlement(context.Background(), testMarketID, pos, num.UintOne(), func(string) bool { return true }) 1051 assert.Equal(t, 2, len(responses)) 1052 resp := responses[0] 1053 assert.NoError(t, err) 1054 // total balance should equal price (only 1 call after all) 1055 assert.Equal(t, price, resp.Balances[0].Balance) 1056 // there should be 2 ledger moves, one from party account, one from insurance acc 1057 assert.Equal(t, 2, len(resp.Entries)) 1058 1059 assert.Equal(t, 2, len(responses[0].Entries)) 1060 assert.Equal(t, num.UintZero(), responses[0].Entries[0].FromAccountBalance) 1061 assert.Equal(t, num.NewUint(500), responses[0].Entries[0].ToAccountBalance) 1062 assert.Equal(t, num.NewUint(4500), responses[0].Entries[1].FromAccountBalance) 1063 assert.Equal(t, num.NewUint(1000), responses[0].Entries[1].ToAccountBalance) 1064 1065 assert.Equal(t, 1, len(responses[1].Entries)) 1066 assert.Equal(t, num.UintZero(), responses[1].Entries[0].FromAccountBalance) 1067 assert.Equal(t, num.NewUint(101000), responses[1].Entries[0].ToAccountBalance) 1068 } 1069 1070 func testTransferLossMissingPartyAccounts(t *testing.T) { 1071 party := "test-party" 1072 price := num.NewUint(1000) 1073 1074 eng := getTestEngine(t) 1075 defer eng.Finish() 1076 1077 // now the positions 1078 pos := []*types.Transfer{ 1079 { 1080 Owner: party, 1081 Amount: &types.FinancialAmount{ 1082 Asset: "BTC", 1083 Amount: price, 1084 }, 1085 Type: types.TransferTypeLoss, 1086 }, 1087 } 1088 resp, err := eng.FinalSettlement(context.Background(), testMarketID, pos, num.UintOne(), func(string) bool { return true }) 1089 assert.Nil(t, resp) 1090 require.Error(t, err) 1091 assert.Contains(t, err.Error(), "account does not exist:") 1092 } 1093 1094 func testProcessBoth(t *testing.T) { 1095 party := "test-party" 1096 moneyParty := "money-party" 1097 price := num.NewUint(1000) 1098 priceX3 := num.Sum(price, price, price) 1099 1100 eng := getTestEngine(t) 1101 defer eng.Finish() 1102 1103 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 1104 insurancePool, err := eng.GetMarketInsurancePoolAccount(testMarketID, testMarketAsset) 1105 assert.Nil(t, err) 1106 err = eng.UpdateBalance(context.Background(), insurancePool.ID, priceX3) 1107 assert.Nil(t, err) 1108 1109 // create party accounts 1110 eng.broker.EXPECT().Send(gomock.Any()).Times(6) 1111 _, _ = eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 1112 _, err = eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 1113 assert.Nil(t, err) 1114 1115 _, _ = eng.CreatePartyGeneralAccount(context.Background(), moneyParty, testMarketAsset) 1116 marginMoneyParty, err := eng.CreatePartyMarginAccount(context.Background(), moneyParty, testMarketID, testMarketAsset) 1117 assert.Nil(t, err) 1118 1119 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 1120 err = eng.IncrementBalance(context.Background(), marginMoneyParty, num.Sum(priceX3, price, price)) 1121 assert.Nil(t, err) 1122 1123 pos := []*types.Transfer{ 1124 { 1125 Owner: party, 1126 Amount: &types.FinancialAmount{ 1127 Amount: price, 1128 Asset: "BTC", 1129 }, 1130 Type: types.TransferTypeLoss, 1131 }, 1132 { 1133 Owner: moneyParty, 1134 Amount: &types.FinancialAmount{ 1135 Amount: price, 1136 Asset: "BTC", 1137 }, 1138 Type: types.TransferTypeLoss, 1139 }, 1140 { 1141 Owner: party, 1142 Amount: &types.FinancialAmount{ 1143 Amount: price, 1144 Asset: "BTC", 1145 }, 1146 Type: types.TransferTypeWin, 1147 }, 1148 { 1149 Owner: moneyParty, 1150 Amount: &types.FinancialAmount{ 1151 Amount: price, 1152 Asset: "BTC", 1153 }, 1154 Type: types.TransferTypeWin, 1155 }, 1156 } 1157 1158 // next up, updating the balance of the parties' general accounts 1159 eng.broker.EXPECT().Send(gomock.Any()).Times(8).Do(func(evt events.Event) { 1160 ae, ok := evt.(accEvt) 1161 assert.True(t, ok) 1162 acc := ae.Account() 1163 if acc.Owner == moneyParty && acc.Type == types.AccountTypeGeneral { 1164 assert.Equal(t, int64(2000), acc.Balance) 1165 } 1166 }) 1167 responses, err := eng.FinalSettlement(context.Background(), testMarketID, pos, num.UintOne(), func(string) bool { return true }) 1168 assert.Equal(t, 4, len(responses)) 1169 assert.NoError(t, err) 1170 resp := responses[0] 1171 // total balance of settlement account should be 3 times price 1172 for _, bal := range resp.Balances { 1173 if bal.Account.Type == types.AccountTypeSettlement { 1174 assert.True(t, bal.Account.Balance.IsZero()) 1175 } 1176 } 1177 // resp = responses[1] 1178 // there should be 3 ledger moves -> settle to party 1, settle to party 2, insurance to party 2 1179 assert.Equal(t, 1, len(responses[0].Entries)) 1180 for _, e := range responses[0].Entries { 1181 assert.Equal(t, num.NewUint(2000), e.FromAccountBalance) 1182 assert.Equal(t, num.NewUint(1000), e.ToAccountBalance) 1183 } 1184 1185 assert.Equal(t, 1, len(responses[1].Entries)) 1186 for _, e := range responses[1].Entries { 1187 assert.Equal(t, num.NewUint(4000), e.FromAccountBalance) 1188 assert.Equal(t, num.NewUint(2000), e.ToAccountBalance) 1189 } 1190 1191 assert.Equal(t, 1, len(responses[2].Entries)) 1192 for _, e := range responses[2].Entries { 1193 assert.Equal(t, num.NewUint(1000), e.FromAccountBalance) 1194 assert.Equal(t, num.NewUint(1000), e.ToAccountBalance) 1195 } 1196 1197 assert.Equal(t, 1, len(responses[3].Entries)) 1198 for _, e := range responses[3].Entries { 1199 assert.Equal(t, num.UintZero(), e.FromAccountBalance) 1200 assert.Equal(t, num.NewUint(5000), e.ToAccountBalance) 1201 } 1202 } 1203 1204 func TestLossSocialization(t *testing.T) { 1205 eng := getTestEngine(t) 1206 defer eng.Finish() 1207 lossParty1 := "lossparty1" 1208 lossParty2 := "lossparty2" 1209 winParty1 := "winparty1" 1210 winParty2 := "winparty2" 1211 1212 // create parties 1213 eng.broker.EXPECT().Send(gomock.Any()).Times(18) 1214 _, _ = eng.CreatePartyGeneralAccount(context.Background(), lossParty1, testMarketAsset) 1215 margin, err := eng.CreatePartyMarginAccount(context.Background(), lossParty1, testMarketID, testMarketAsset) 1216 eng.IncrementBalance(context.Background(), margin, num.NewUint(500)) 1217 assert.Nil(t, err) 1218 _, _ = eng.CreatePartyGeneralAccount(context.Background(), lossParty2, testMarketAsset) 1219 margin, err = eng.CreatePartyMarginAccount(context.Background(), lossParty2, testMarketID, testMarketAsset) 1220 eng.IncrementBalance(context.Background(), margin, num.NewUint(1100)) 1221 assert.Nil(t, err) 1222 _, _ = eng.CreatePartyGeneralAccount(context.Background(), winParty1, testMarketAsset) 1223 _, err = eng.CreatePartyMarginAccount(context.Background(), winParty1, testMarketID, testMarketAsset) 1224 assert.Nil(t, err) 1225 _, _ = eng.CreatePartyGeneralAccount(context.Background(), winParty2, testMarketAsset) 1226 _, err = eng.CreatePartyMarginAccount(context.Background(), winParty2, testMarketID, testMarketAsset) 1227 assert.Nil(t, err) 1228 1229 transfers := []*types.Transfer{ 1230 { 1231 Owner: lossParty1, 1232 Amount: &types.FinancialAmount{ 1233 Amount: num.NewUint(700), 1234 Asset: testMarketAsset, 1235 }, 1236 Type: types.TransferTypeLoss, 1237 }, 1238 { 1239 Owner: lossParty2, 1240 Amount: &types.FinancialAmount{ 1241 Amount: num.NewUint(1400), 1242 Asset: testMarketAsset, 1243 }, 1244 Type: types.TransferTypeLoss, 1245 }, 1246 { 1247 Owner: winParty1, 1248 Amount: &types.FinancialAmount{ 1249 Amount: num.NewUint(1400), 1250 Asset: testMarketAsset, 1251 }, 1252 Type: types.TransferTypeWin, 1253 }, 1254 { 1255 Owner: winParty2, 1256 Amount: &types.FinancialAmount{ 1257 Amount: num.NewUint(700), 1258 Asset: testMarketAsset, 1259 }, 1260 Type: types.TransferTypeWin, 1261 }, 1262 } 1263 1264 eng.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() 1265 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes().Do(func(evt events.Event) { 1266 ae, ok := evt.(accEvt) 1267 assert.True(t, ok) 1268 acc := ae.Account() 1269 if acc.Owner == winParty1 && acc.Type == types.AccountTypeMargin { 1270 assert.Equal(t, 1066, stringToInt(acc.Balance)) 1271 } 1272 if acc.Owner == winParty2 && acc.Type == types.AccountTypeMargin { 1273 assert.Equal(t, 534, stringToInt(acc.Balance)) 1274 } 1275 }) 1276 raw, err := eng.FinalSettlement(context.Background(), testMarketID, transfers, num.UintOne(), func(string) bool { return true }) 1277 assert.NoError(t, err) 1278 assert.Equal(t, 4, len(raw)) 1279 1280 assert.Equal(t, 1, len(raw[0].Entries)) 1281 assert.Equal(t, num.NewUint(500), raw[0].Entries[0].ToAccountBalance) 1282 assert.Equal(t, 1, len(raw[1].Entries)) 1283 assert.Equal(t, num.NewUint(1600), raw[1].Entries[0].ToAccountBalance) 1284 assert.Equal(t, 1, len(raw[2].Entries)) 1285 assert.Equal(t, num.NewUint(1066), raw[2].Entries[0].ToAccountBalance) 1286 assert.Equal(t, 1, len(raw[3].Entries)) 1287 assert.Equal(t, num.NewUint(534), raw[3].Entries[0].ToAccountBalance) 1288 } 1289 1290 func testSettleBalanceNotZero(t *testing.T) { 1291 party := "test-party" 1292 moneyParty := "money-party" 1293 price := num.NewUint(1000) 1294 1295 eng := getTestEngine(t) 1296 defer eng.Finish() 1297 1298 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 1299 insurancePool, err := eng.GetMarketInsurancePoolAccount(testMarketID, testMarketAsset) 1300 assert.Nil(t, err) 1301 err = eng.UpdateBalance(context.Background(), insurancePool.ID, num.UintZero().Div(price, num.NewUint(2))) 1302 assert.Nil(t, err) 1303 1304 // create party accounts 1305 eng.broker.EXPECT().Send(gomock.Any()).Times(8) 1306 gID, _ := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 1307 mID, err := eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 1308 assert.Nil(t, err) 1309 1310 assert.NotEmpty(t, mID) 1311 assert.NotEmpty(t, gID) 1312 1313 // create + add balance 1314 _, _ = eng.CreatePartyGeneralAccount(context.Background(), moneyParty, testMarketAsset) 1315 marginMoneyParty, err := eng.CreatePartyMarginAccount(context.Background(), moneyParty, testMarketID, testMarketAsset) 1316 assert.Nil(t, err) 1317 1318 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 1319 err = eng.UpdateBalance(context.Background(), marginMoneyParty, num.UintZero().Mul(num.NewUint(6), price)) 1320 assert.Nil(t, err) 1321 pos := []*types.Transfer{ 1322 { 1323 Owner: moneyParty, 1324 Amount: &types.FinancialAmount{ 1325 Amount: num.UintZero().Mul(price, num.NewUint(2)), // lost 2xprice, party only won half 1326 Asset: "BTC", 1327 }, 1328 Type: types.TransferTypeMTMLoss, 1329 }, 1330 { 1331 Owner: party, 1332 Amount: &types.FinancialAmount{ 1333 Amount: price, 1334 Asset: "BTC", 1335 }, 1336 Type: types.TransferTypeMTMWin, 1337 }, 1338 } 1339 1340 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 1341 eng.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() 1342 transfers := eng.getTestMTMTransfer(pos) 1343 defer func() { 1344 r := recover() 1345 require.NotNil(t, r) 1346 }() 1347 _, _, _ = eng.MarkToMarket(context.Background(), testMarketID, transfers, "BTC", func(string) bool { return true }) 1348 // this should return an error 1349 } 1350 1351 func testProcessBothProRated(t *testing.T) { 1352 party := "test-party" 1353 moneyParty := "money-party" 1354 price := num.NewUint(1000) 1355 1356 eng := getTestEngine(t) 1357 defer eng.Finish() 1358 1359 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 1360 insurancePool, err := eng.GetMarketInsurancePoolAccount(testMarketID, testMarketAsset) 1361 assert.Nil(t, err) 1362 err = eng.UpdateBalance(context.Background(), insurancePool.ID, num.UintZero().Div(price, num.NewUint(2))) 1363 assert.Nil(t, err) 1364 1365 // create party accounts 1366 eng.broker.EXPECT().Send(gomock.Any()).Times(8) 1367 _, _ = eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 1368 _, err = eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 1369 assert.Nil(t, err) 1370 1371 _, _ = eng.CreatePartyGeneralAccount(context.Background(), moneyParty, testMarketAsset) 1372 marginMoneyParty, err := eng.CreatePartyMarginAccount(context.Background(), moneyParty, testMarketID, testMarketAsset) 1373 assert.Nil(t, err) 1374 1375 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 1376 err = eng.IncrementBalance(context.Background(), marginMoneyParty, num.UintZero().Mul(price, num.NewUint(5))) 1377 assert.Nil(t, err) 1378 1379 pos := []*types.Transfer{ 1380 { 1381 Owner: party, 1382 Amount: &types.FinancialAmount{ 1383 Amount: price, 1384 Asset: "BTC", 1385 }, 1386 Type: types.TransferTypeLoss, 1387 }, 1388 { 1389 Owner: moneyParty, 1390 Amount: &types.FinancialAmount{ 1391 Amount: price, 1392 Asset: "BTC", 1393 }, 1394 Type: types.TransferTypeLoss, 1395 }, 1396 { 1397 Owner: party, 1398 Amount: &types.FinancialAmount{ 1399 Amount: price, 1400 Asset: "BTC", 1401 }, 1402 Type: types.TransferTypeWin, 1403 }, 1404 { 1405 Owner: moneyParty, 1406 Amount: &types.FinancialAmount{ 1407 Amount: price, 1408 Asset: "BTC", 1409 }, 1410 Type: types.TransferTypeWin, 1411 }, 1412 } 1413 1414 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 1415 eng.broker.EXPECT().SendBatch(gomock.Any()).Times(2) 1416 responses, err := eng.FinalSettlement(context.Background(), testMarketID, pos, num.UintOne(), func(string) bool { return true }) 1417 assert.Equal(t, 4, len(responses)) 1418 assert.NoError(t, err) 1419 1420 // there should be 3 ledger moves -> settle to party 1, settle to party 2, insurance to party 2 1421 assert.Equal(t, 1, len(responses[0].Entries)) 1422 assert.Equal(t, num.NewUint(500), responses[0].Entries[0].ToAccountBalance) 1423 assert.Equal(t, num.NewUint(500), responses[0].Entries[0].ToAccountBalance) 1424 1425 assert.Equal(t, 1, len(responses[1].Entries)) 1426 assert.Equal(t, num.NewUint(1500), responses[1].Entries[0].ToAccountBalance) 1427 assert.Equal(t, num.NewUint(1500), responses[1].Entries[0].ToAccountBalance) 1428 1429 assert.Equal(t, 1, len(responses[2].Entries)) 1430 assert.Equal(t, num.NewUint(750), responses[2].Entries[0].ToAccountBalance) 1431 assert.Equal(t, num.NewUint(750), responses[2].Entries[0].ToAccountBalance) 1432 1433 assert.Equal(t, 1, len(responses[3].Entries)) 1434 assert.Equal(t, num.NewUint(4750), responses[3].Entries[0].ToAccountBalance) 1435 assert.Equal(t, num.NewUint(4750), responses[3].Entries[0].ToAccountBalance) 1436 } 1437 1438 func testProcessBothProRatedMTM(t *testing.T) { 1439 party := "test-party" 1440 moneyParty := "money-party" 1441 price := num.NewUint(1000) 1442 1443 eng := getTestEngine(t) 1444 defer eng.Finish() 1445 1446 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 1447 insurancePool, err := eng.GetMarketInsurancePoolAccount(testMarketID, testMarketAsset) 1448 assert.Nil(t, err) 1449 err = eng.UpdateBalance(context.Background(), insurancePool.ID, num.UintZero().Div(price, num.NewUint(2))) 1450 assert.Nil(t, err) 1451 1452 // create party accounts 1453 eng.broker.EXPECT().Send(gomock.Any()).Times(8) 1454 _, _ = eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 1455 _, err = eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 1456 assert.Nil(t, err) 1457 1458 _, _ = eng.CreatePartyGeneralAccount(context.Background(), moneyParty, testMarketAsset) 1459 marginMoneyParty, err := eng.CreatePartyMarginAccount(context.Background(), moneyParty, testMarketID, testMarketAsset) 1460 assert.Nil(t, err) 1461 1462 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 1463 err = eng.IncrementBalance(context.Background(), marginMoneyParty, num.UintZero().Mul(price, num.NewUint(5))) 1464 assert.Nil(t, err) 1465 1466 pos := []*types.Transfer{ 1467 { 1468 Owner: party, 1469 Amount: &types.FinancialAmount{ 1470 Amount: price, 1471 Asset: "BTC", 1472 }, 1473 Type: types.TransferTypeMTMLoss, 1474 }, 1475 { 1476 Owner: moneyParty, 1477 Amount: &types.FinancialAmount{ 1478 Amount: price, 1479 Asset: "BTC", 1480 }, 1481 Type: types.TransferTypeMTMLoss, 1482 }, 1483 { 1484 Owner: party, 1485 Amount: &types.FinancialAmount{ 1486 Amount: price, 1487 Asset: "BTC", 1488 }, 1489 Type: types.TransferTypeMTMWin, 1490 }, 1491 { 1492 Owner: moneyParty, 1493 Amount: &types.FinancialAmount{ 1494 Amount: price, 1495 Asset: "BTC", 1496 }, 1497 Type: types.TransferTypeMTMWin, 1498 }, 1499 } 1500 1501 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 1502 eng.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() 1503 // quickly get the interface mocked for this test 1504 transfers := getMTMTransfer(pos) 1505 responses, raw, err := eng.MarkToMarket(context.Background(), testMarketID, transfers, "BTC", func(string) bool { return true }) 1506 assert.Equal(t, 4, len(responses)) 1507 assert.NoError(t, err, "was error") 1508 assert.NotEmpty(t, raw) 1509 1510 // there should be 3 ledger moves -> settle to party 1, settle to party 2, insurance to party 2 1511 assert.Equal(t, 1, len(raw[1].Entries)) 1512 } 1513 1514 func testRemoveDistressedBalance(t *testing.T) { 1515 party := "test-party" 1516 1517 insBalance := num.NewUint(1000) 1518 eng := getTestEngine(t) 1519 defer eng.Finish() 1520 1521 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 1522 insurancePool, err := eng.GetMarketInsurancePoolAccount(testMarketID, testMarketAsset) 1523 assert.Nil(t, err) 1524 err = eng.UpdateBalance(context.Background(), insurancePool.ID, insBalance) 1525 assert.Nil(t, err) 1526 1527 // create party accounts (calls buf.Add twice), and add balance (calls it a third time) 1528 eng.broker.EXPECT().Send(gomock.Any()).Times(3) 1529 _, _ = eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 1530 marginID, err := eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 1531 assert.Nil(t, err) 1532 1533 // add balance to margin account for party 1534 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 1535 err = eng.IncrementBalance(context.Background(), marginID, num.NewUint(100)) 1536 assert.Nil(t, err) 1537 1538 // events: 1539 data := []events.MarketPosition{ 1540 marketPositionFake{ 1541 party: party, 1542 }, 1543 } 1544 eng.broker.EXPECT().Send(gomock.Any()).Times(2).Do(func(evt events.Event) { 1545 ae, ok := evt.(accEvt) 1546 assert.True(t, ok) 1547 acc := ae.Account() 1548 if acc.Id == marginID { 1549 assert.Zero(t, stringToInt(acc.Balance)) 1550 } else { 1551 // this doesn't happen yet 1552 assert.Equal(t, num.UintZero().Add(insBalance, num.NewUint(100)).String(), acc.Balance) 1553 } 1554 }) 1555 resp, err := eng.RemoveDistressed(context.Background(), data, testMarketID, testMarketAsset, func(string) bool { return true }) 1556 assert.NoError(t, err) 1557 assert.Equal(t, 1, len(resp.Entries)) 1558 1559 // check if account was deleted 1560 _, err = eng.GetAccountByID(marginID) 1561 require.Error(t, err) 1562 assert.Contains(t, err.Error(), "account does not exist:") 1563 } 1564 1565 func testRemoveDistressedNoBalance(t *testing.T) { 1566 party := "test-party" 1567 1568 insBalance := num.NewUint(1000) 1569 eng := getTestEngine(t) 1570 defer eng.Finish() 1571 1572 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 1573 insurancePool, err := eng.GetMarketInsurancePoolAccount(testMarketID, testMarketAsset) 1574 assert.Nil(t, err) 1575 err = eng.UpdateBalance(context.Background(), insurancePool.ID, insBalance) 1576 assert.Nil(t, err) 1577 1578 // create party accounts (calls buf.Add twice), and add balance (calls it a third time) 1579 eng.broker.EXPECT().Send(gomock.Any()).Times(3) 1580 _, _ = eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 1581 marginID, err := eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 1582 assert.Nil(t, err) 1583 1584 // no balance on margin account, so we don't expect there to be any balance updates in the buffer either 1585 // set up calls expected to buffer: add the update of the balance, of system account (insurance) and one with the margin account set to 0 1586 data := []events.MarketPosition{ 1587 marketPositionFake{ 1588 party: party, 1589 }, 1590 } 1591 resp, err := eng.RemoveDistressed(context.Background(), data, testMarketID, testMarketAsset, func(string) bool { return true }) 1592 assert.NoError(t, err) 1593 assert.Equal(t, 0, len(resp.Entries)) 1594 1595 // check if account was deleted 1596 _, err = eng.GetAccountByID(marginID) 1597 require.Error(t, err) 1598 assert.Contains(t, err.Error(), "account does not exist:") 1599 } 1600 1601 func testPerpFundingSuccess(t *testing.T) { 1602 party := "test-party" 1603 moneyParty := "money-party" 1604 amount := num.NewUint(1000) 1605 1606 eng := getTestEngine(t) 1607 defer eng.Finish() 1608 1609 _, settleAccountID, err := eng.CreateMarketAccounts(context.Background(), testMarketID, testMarketAsset) 1610 assert.NoError(t, err) 1611 1612 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 1613 insurancePool, err := eng.GetMarketInsurancePoolAccount(testMarketID, testMarketAsset) 1614 assert.Nil(t, err) 1615 err = eng.UpdateBalance(context.Background(), insurancePool.ID, num.UintZero().Div(amount, num.NewUint(2))) 1616 assert.Nil(t, err) 1617 1618 // create party accounts 1619 eng.broker.EXPECT().Send(gomock.Any()).Times(8) 1620 gID, _ := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 1621 mID, err := eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 1622 assert.Nil(t, err) 1623 1624 assert.NotEmpty(t, mID) 1625 assert.NotEmpty(t, gID) 1626 1627 // create + add balance 1628 _, _ = eng.CreatePartyGeneralAccount(context.Background(), moneyParty, testMarketAsset) 1629 marginMoneyParty, err := eng.CreatePartyMarginAccount(context.Background(), moneyParty, testMarketID, testMarketAsset) 1630 assert.Nil(t, err) 1631 1632 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 1633 err = eng.UpdateBalance(context.Background(), marginMoneyParty, num.UintZero().Mul(num.NewUint(5), amount)) 1634 assert.Nil(t, err) 1635 pos := []*types.Transfer{ 1636 { 1637 Owner: party, 1638 Amount: &types.FinancialAmount{ 1639 Amount: amount, 1640 Asset: testMarketAsset, 1641 }, 1642 Type: types.TransferTypePerpFundingLoss, 1643 }, 1644 { 1645 Owner: moneyParty, 1646 Amount: &types.FinancialAmount{ 1647 Amount: amount, 1648 Asset: testMarketAsset, 1649 }, 1650 Type: types.TransferTypePerpFundingLoss, 1651 }, 1652 { 1653 Owner: party, 1654 Amount: &types.FinancialAmount{ 1655 Amount: amount, 1656 Asset: testMarketAsset, 1657 }, 1658 Type: types.TransferTypePerpFundingWin, 1659 }, 1660 { 1661 Owner: moneyParty, 1662 Amount: &types.FinancialAmount{ 1663 Amount: amount, 1664 Asset: testMarketAsset, 1665 }, 1666 Type: types.TransferTypePerpFundingWin, 1667 }, 1668 } 1669 1670 eng.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() 1671 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes().Do(func(evt events.Event) { 1672 ae, ok := evt.(accEvt) 1673 assert.True(t, ok) 1674 acc := ae.Account() 1675 if acc.Owner == party && acc.Type == types.AccountTypeGeneral { 1676 assert.Equal(t, acc.Balance, int64(833)) 1677 } 1678 if acc.Owner == moneyParty && acc.Type == types.AccountTypeGeneral { 1679 assert.Equal(t, acc.Balance, int64(1666)) 1680 } 1681 }) 1682 transfers := eng.getTestMTMTransfer(pos) 1683 evts, raw, err := eng.PerpsFundingSettlement(context.Background(), testMarketID, transfers, testMarketAsset, nil, func(string) bool { return true }) 1684 assert.NoError(t, err) 1685 assert.Equal(t, 4, len(raw)) 1686 assert.NotEmpty(t, evts) 1687 1688 // handle losers: expect losers to pay to settlement account 1689 // party doesn't have any money so we expect to drain the insurance pool instead 1690 assert.Equal(t, 1, len(raw[0].Entries)) 1691 assert.Equal(t, settleAccountID, raw[0].Entries[0].ToAccount.ID()) 1692 assert.Equal(t, insurancePool.ID, raw[0].Entries[0].FromAccount.ID()) 1693 assert.Equal(t, num.NewUint(500), raw[0].Entries[0].Amount) 1694 1695 // this guy had money so their loss is moved to the settlement account 1696 assert.Equal(t, 1, len(raw[1].Entries)) 1697 assert.Equal(t, settleAccountID, raw[1].Entries[0].ToAccount.ID()) 1698 assert.Equal(t, moneyParty, raw[1].Entries[0].FromAccount.Owner) 1699 assert.Equal(t, num.NewUint(1000), raw[1].Entries[0].Amount) 1700 1701 // handle wins: each winner will get 750, which is half off 1000 + 500 1702 assert.Equal(t, 1, len(raw[2].Entries)) 1703 assert.Equal(t, settleAccountID, raw[2].Entries[0].FromAccount.ID()) 1704 assert.Equal(t, party, raw[2].Entries[0].ToAccount.Owner) 1705 assert.Equal(t, num.NewUint(750), raw[2].Entries[0].Amount) 1706 1707 assert.Equal(t, 1, len(raw[3].Entries)) 1708 assert.Equal(t, settleAccountID, raw[3].Entries[0].FromAccount.ID()) 1709 assert.Equal(t, moneyParty, raw[3].Entries[0].ToAccount.Owner) 1710 assert.Equal(t, num.NewUint(750), raw[3].Entries[0].Amount) 1711 } 1712 1713 func testPerpFundingSuccessWithRound(t *testing.T) { 1714 party := "test-party" 1715 moneyParty := "money-party" 1716 amount := num.NewUint(1000) 1717 round := num.UintOne() 1718 1719 eng := getTestEngine(t) 1720 defer eng.Finish() 1721 1722 _, settleAccountID, err := eng.CreateMarketAccounts(context.Background(), testMarketID, testMarketAsset) 1723 assert.NoError(t, err) 1724 1725 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 1726 insurancePool, err := eng.GetMarketInsurancePoolAccount(testMarketID, testMarketAsset) 1727 assert.Nil(t, err) 1728 err = eng.UpdateBalance(context.Background(), insurancePool.ID, num.UintZero().Div(amount, num.NewUint(2))) 1729 assert.Nil(t, err) 1730 1731 // create party accounts 1732 eng.broker.EXPECT().Send(gomock.Any()).Times(8) 1733 gID, _ := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 1734 mID, err := eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 1735 assert.Nil(t, err) 1736 1737 assert.NotEmpty(t, mID) 1738 assert.NotEmpty(t, gID) 1739 1740 // create + add balance 1741 _, _ = eng.CreatePartyGeneralAccount(context.Background(), moneyParty, testMarketAsset) 1742 marginMoneyParty, err := eng.CreatePartyMarginAccount(context.Background(), moneyParty, testMarketID, testMarketAsset) 1743 assert.Nil(t, err) 1744 1745 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 1746 err = eng.UpdateBalance(context.Background(), marginMoneyParty, num.UintZero().Mul(num.NewUint(5), amount)) 1747 assert.Nil(t, err) 1748 pos := []*types.Transfer{ 1749 { 1750 Owner: moneyParty, 1751 Amount: &types.FinancialAmount{ 1752 Amount: amount, 1753 Asset: testMarketAsset, 1754 }, 1755 Type: types.TransferTypePerpFundingLoss, 1756 }, 1757 { 1758 Owner: party, 1759 Amount: &types.FinancialAmount{ 1760 Amount: amount.Clone().Sub(amount, round), // win amount is a little less than lose, assume some rounding issue occurred 1761 Asset: testMarketAsset, 1762 }, 1763 Type: types.TransferTypePerpFundingWin, 1764 }, 1765 } 1766 1767 eng.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() 1768 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes().Do(func(evt events.Event) { 1769 ae, ok := evt.(accEvt) 1770 assert.True(t, ok) 1771 acc := ae.Account() 1772 if acc.Owner == party && acc.Type == types.AccountTypeGeneral { 1773 assert.Equal(t, acc.Balance, int64(833)) 1774 } 1775 if acc.Owner == moneyParty && acc.Type == types.AccountTypeGeneral { 1776 assert.Equal(t, acc.Balance, int64(1666)) 1777 } 1778 }) 1779 transfers := eng.getTestMTMTransfer(pos) 1780 evts, raw, err := eng.PerpsFundingSettlement(context.Background(), testMarketID, transfers, testMarketAsset, round, func(string) bool { return true }) 1781 assert.NoError(t, err) 1782 assert.Equal(t, 3, len(raw)) 1783 assert.NotEmpty(t, evts) 1784 1785 // handle losers: expect losers to pay to settlement account 1786 assert.Equal(t, 1, len(raw[0].Entries)) 1787 assert.Equal(t, settleAccountID, raw[0].Entries[0].ToAccount.ID()) 1788 assert.Equal(t, moneyParty, raw[0].Entries[0].FromAccount.Owner) 1789 assert.Equal(t, num.NewUint(1000), raw[0].Entries[0].Amount) 1790 1791 // handle winners: gets 999 and the left over in the settlement account is ok 1792 assert.Equal(t, 1, len(raw[1].Entries)) 1793 assert.Equal(t, party, raw[1].Entries[0].ToAccount.Owner) 1794 assert.Equal(t, settleAccountID, raw[1].Entries[0].FromAccount.ID()) 1795 assert.Equal(t, num.NewUint(999), raw[1].Entries[0].Amount) 1796 1797 // the round goes into global insurance 1798 assert.Equal(t, 1, len(raw[2].Entries)) 1799 assert.Equal(t, insurancePool.ID, raw[2].Entries[0].ToAccount.ID()) 1800 assert.Equal(t, settleAccountID, raw[2].Entries[0].FromAccount.ID()) 1801 assert.Equal(t, num.NewUint(1), raw[2].Entries[0].Amount) 1802 } 1803 1804 // most of this function is copied from the MarkToMarket test - we're using channels, sure 1805 // but the flow should remain the same regardless. 1806 func testMTMSuccess(t *testing.T) { 1807 party := "test-party" 1808 moneyParty := "money-party" 1809 price := num.NewUint(1000) 1810 1811 eng := getTestEngine(t) 1812 defer eng.Finish() 1813 1814 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 1815 insurancePool, err := eng.GetMarketInsurancePoolAccount(testMarketID, testMarketAsset) 1816 assert.Nil(t, err) 1817 err = eng.UpdateBalance(context.Background(), insurancePool.ID, num.UintZero().Div(price, num.NewUint(2))) 1818 assert.Nil(t, err) 1819 1820 // create party accounts 1821 eng.broker.EXPECT().Send(gomock.Any()).Times(8) 1822 gID, _ := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 1823 mID, err := eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 1824 assert.Nil(t, err) 1825 1826 assert.NotEmpty(t, mID) 1827 assert.NotEmpty(t, gID) 1828 1829 // create + add balance 1830 _, _ = eng.CreatePartyGeneralAccount(context.Background(), moneyParty, testMarketAsset) 1831 marginMoneyParty, err := eng.CreatePartyMarginAccount(context.Background(), moneyParty, testMarketID, testMarketAsset) 1832 assert.Nil(t, err) 1833 1834 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 1835 err = eng.UpdateBalance(context.Background(), marginMoneyParty, num.UintZero().Mul(num.NewUint(5), price)) 1836 assert.Nil(t, err) 1837 1838 pos := []*types.Transfer{ 1839 { 1840 Owner: party, 1841 Amount: &types.FinancialAmount{ 1842 Amount: price, 1843 Asset: testMarketAsset, 1844 }, 1845 Type: types.TransferTypeMTMLoss, 1846 }, 1847 { 1848 Owner: moneyParty, 1849 Amount: &types.FinancialAmount{ 1850 Amount: price, 1851 Asset: testMarketAsset, 1852 }, 1853 Type: types.TransferTypeMTMLoss, 1854 }, 1855 { 1856 Owner: party, 1857 Amount: &types.FinancialAmount{ 1858 Amount: price, 1859 Asset: testMarketAsset, 1860 }, 1861 Type: types.TransferTypeMTMWin, 1862 }, 1863 { 1864 Owner: moneyParty, 1865 Amount: &types.FinancialAmount{ 1866 Amount: price, 1867 Asset: testMarketAsset, 1868 }, 1869 Type: types.TransferTypeMTMWin, 1870 }, 1871 } 1872 1873 eng.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() 1874 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes().Do(func(evt events.Event) { 1875 ae, ok := evt.(accEvt) 1876 assert.True(t, ok) 1877 acc := ae.Account() 1878 if acc.Owner == party && acc.Type == types.AccountTypeGeneral { 1879 assert.Equal(t, acc.Balance, int64(833)) 1880 } 1881 if acc.Owner == moneyParty && acc.Type == types.AccountTypeGeneral { 1882 assert.Equal(t, acc.Balance, int64(1666)) 1883 } 1884 }) 1885 transfers := eng.getTestMTMTransfer(pos) 1886 evts, raw, err := eng.MarkToMarket(context.Background(), testMarketID, transfers, "BTC", func(string) bool { return true }) 1887 assert.NoError(t, err) 1888 assert.Equal(t, 4, len(raw)) 1889 assert.NotEmpty(t, evts) 1890 } 1891 1892 func TestInvalidMarketID(t *testing.T) { 1893 party := "test-party" 1894 price := num.NewUint(1000) 1895 1896 eng := getTestEngine(t) 1897 defer eng.Finish() 1898 1899 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 1900 insurancePool, err := eng.GetMarketInsurancePoolAccount(testMarketID, testMarketAsset) 1901 assert.Nil(t, err) 1902 err = eng.UpdateBalance(context.Background(), insurancePool.ID, num.UintZero().Div(price, num.NewUint(2))) 1903 assert.Nil(t, err) 1904 1905 // create party accounts 1906 eng.broker.EXPECT().Send(gomock.Any()).Times(3) 1907 _, _ = eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 1908 _, err = eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 1909 assert.Nil(t, err) 1910 1911 pos := []*types.Transfer{ 1912 { 1913 Owner: party, 1914 Amount: &types.FinancialAmount{ 1915 Amount: price, 1916 Asset: testMarketAsset, 1917 }, 1918 Type: types.TransferTypeMTMLoss, 1919 }, 1920 } 1921 transfers := eng.getTestMTMTransfer(pos) 1922 1923 invalidMarketID := testMarketID + "invalid" 1924 evts, raw, err := eng.MarkToMarket(context.Background(), invalidMarketID, transfers, "BTC", func(string) bool { return true }) 1925 assert.Error(t, err) 1926 assert.Equal(t, 0, len(raw)) 1927 assert.Empty(t, evts) 1928 } 1929 1930 func TestEmptyTransfer(t *testing.T) { 1931 party := "test-party" 1932 price := num.NewUint(1000) 1933 1934 eng := getTestEngine(t) 1935 defer eng.Finish() 1936 1937 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 1938 insurancePool, err := eng.GetMarketInsurancePoolAccount(testMarketID, testMarketAsset) 1939 assert.Nil(t, err) 1940 err = eng.UpdateBalance(context.Background(), insurancePool.ID, num.UintZero().Div(price, num.NewUint(2))) 1941 assert.Nil(t, err) 1942 1943 // create party accounts 1944 eng.broker.EXPECT().Send(gomock.Any()).Times(3) 1945 _, _ = eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 1946 _, err = eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 1947 assert.Nil(t, err) 1948 1949 pos := []*types.Transfer{ 1950 { 1951 Owner: party, 1952 Amount: &types.FinancialAmount{ 1953 Amount: num.UintZero(), 1954 Asset: testMarketAsset, 1955 }, 1956 Type: types.TransferTypeMTMLoss, 1957 }, 1958 } 1959 transfers := eng.getTestMTMTransfer(pos) 1960 1961 evts, raw, err := eng.MarkToMarket(context.Background(), testMarketID, transfers, "BTC", func(string) bool { return true }) 1962 assert.NoError(t, err) 1963 assert.Equal(t, 0, len(raw)) 1964 assert.Empty(t, evts) 1965 } 1966 1967 func TestNoMarginAccount(t *testing.T) { 1968 party := "test-party" 1969 price := num.NewUint(1000) 1970 1971 eng := getTestEngine(t) 1972 defer eng.Finish() 1973 1974 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 1975 insurancePool, err := eng.GetMarketInsurancePoolAccount(testMarketID, testMarketAsset) 1976 assert.Nil(t, err) 1977 err = eng.UpdateBalance(context.Background(), insurancePool.ID, num.UintZero().Div(price, num.NewUint(2))) 1978 assert.Nil(t, err) 1979 1980 // create party accounts 1981 eng.broker.EXPECT().Send(gomock.Any()).Times(2) 1982 _, _ = eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 1983 1984 pos := []*types.Transfer{ 1985 { 1986 Owner: party, 1987 Amount: &types.FinancialAmount{ 1988 Amount: price, 1989 Asset: testMarketAsset, 1990 }, 1991 Type: types.TransferTypeMTMLoss, 1992 }, 1993 } 1994 transfers := eng.getTestMTMTransfer(pos) 1995 1996 evts, raw, err := eng.MarkToMarket(context.Background(), testMarketID, transfers, "BTC", func(string) bool { return true }) 1997 assert.Error(t, err) 1998 assert.Equal(t, 0, len(raw)) 1999 assert.Empty(t, evts) 2000 } 2001 2002 func TestNoGeneralAccount(t *testing.T) { 2003 party := "test-party" 2004 price := num.NewUint(1000) 2005 2006 eng := getTestEngine(t) 2007 defer eng.Finish() 2008 2009 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 2010 insurancePool, err := eng.GetMarketInsurancePoolAccount(testMarketID, testMarketAsset) 2011 assert.Nil(t, err) 2012 err = eng.UpdateBalance(context.Background(), insurancePool.ID, num.UintZero().Div(price, num.NewUint(2))) 2013 assert.Nil(t, err) 2014 2015 pos := []*types.Transfer{ 2016 { 2017 Owner: party, 2018 Amount: &types.FinancialAmount{ 2019 Amount: price, 2020 Asset: testMarketAsset, 2021 }, 2022 Type: types.TransferTypeMTMLoss, 2023 }, 2024 } 2025 transfers := eng.getTestMTMTransfer(pos) 2026 2027 evts, raw, err := eng.MarkToMarket(context.Background(), testMarketID, transfers, "BTC", func(string) bool { return true }) 2028 assert.Error(t, err) 2029 assert.Equal(t, 0, len(raw)) 2030 assert.Empty(t, evts) 2031 } 2032 2033 func TestMTMNoTransfers(t *testing.T) { 2034 price := num.NewUint(1000) 2035 2036 eng := getTestEngine(t) 2037 defer eng.Finish() 2038 2039 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 2040 insurancePool, err := eng.GetMarketInsurancePoolAccount(testMarketID, testMarketAsset) 2041 assert.Nil(t, err) 2042 err = eng.UpdateBalance(context.Background(), insurancePool.ID, num.UintZero().Div(price, num.NewUint(2))) 2043 assert.Nil(t, err) 2044 2045 pos := []*types.Transfer{} 2046 transfers := eng.getTestMTMTransfer(pos) 2047 2048 // Empty list of transfers 2049 evts, raw, err := eng.MarkToMarket(context.Background(), testMarketID, transfers, "BTC", func(string) bool { return true }) 2050 assert.NoError(t, err) 2051 assert.Equal(t, 0, len(raw)) 2052 assert.Empty(t, evts) 2053 2054 // List with a single nil value 2055 mt := mtmFake{ 2056 t: nil, 2057 party: "test-party", 2058 } 2059 transfers = append(transfers, mt) 2060 evts, raw, err = eng.MarkToMarket(context.Background(), testMarketID, transfers, "BTC", func(string) bool { return true }) 2061 assert.NoError(t, err) 2062 assert.Equal(t, 0, len(raw)) 2063 assert.Equal(t, len(evts), 1) 2064 } 2065 2066 func TestFinalSettlementNoTransfers(t *testing.T) { 2067 price := num.NewUint(1000) 2068 2069 eng := getTestEngine(t) 2070 defer eng.Finish() 2071 2072 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 2073 insurancePool, err := eng.GetMarketInsurancePoolAccount(testMarketID, testMarketAsset) 2074 assert.Nil(t, err) 2075 err = eng.UpdateBalance(context.Background(), insurancePool.ID, num.UintZero().Div(price, num.NewUint(2))) 2076 assert.Nil(t, err) 2077 2078 pos := []*types.Transfer{} 2079 2080 responses, err := eng.FinalSettlement(context.Background(), testMarketID, pos, num.UintOne(), func(string) bool { return true }) 2081 assert.NoError(t, err) 2082 assert.Equal(t, 0, len(responses)) 2083 } 2084 2085 func TestFinalSettlementNoSystemAccounts(t *testing.T) { 2086 price := num.NewUint(1000) 2087 2088 eng := getTestEngine(t) 2089 defer eng.Finish() 2090 2091 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 2092 insurancePool, err := eng.GetMarketInsurancePoolAccount(testMarketID, testMarketAsset) 2093 assert.Nil(t, err) 2094 err = eng.UpdateBalance(context.Background(), insurancePool.ID, num.UintZero().Div(price, num.NewUint(2))) 2095 assert.Nil(t, err) 2096 2097 pos := []*types.Transfer{ 2098 { 2099 Owner: "testParty", 2100 Amount: &types.FinancialAmount{ 2101 Amount: price, 2102 Asset: "BTC", 2103 }, 2104 Type: types.TransferTypeLoss, 2105 }, 2106 } 2107 2108 responses, err := eng.FinalSettlement(context.Background(), "invalidMarketID", pos, num.UintOne(), func(string) bool { return true }) 2109 assert.Error(t, err) 2110 assert.Equal(t, 0, len(responses)) 2111 } 2112 2113 func TestFinalSettlementNotEnoughMargin(t *testing.T) { 2114 amount := num.NewUint(1000) 2115 2116 eng := getTestEngine(t) 2117 defer eng.Finish() 2118 2119 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 2120 insurancePool, err := eng.GetMarketInsurancePoolAccount(testMarketID, testMarketAsset) 2121 assert.Nil(t, err) 2122 err = eng.UpdateBalance(context.Background(), insurancePool.ID, num.UintZero().Div(amount, num.NewUint(2))) 2123 assert.Nil(t, err) 2124 2125 eng.broker.EXPECT().Send(gomock.Any()).Times(4) 2126 _, _ = eng.CreatePartyGeneralAccount(context.Background(), "testParty", testMarketAsset) 2127 _, err = eng.CreatePartyMarginAccount(context.Background(), "testParty", testMarketID, testMarketAsset) 2128 require.NoError(t, err) 2129 2130 pos := []*types.Transfer{ 2131 { 2132 Owner: "testParty", 2133 Amount: &types.FinancialAmount{ 2134 Amount: num.UintZero().Mul(amount, num.NewUint(100)), 2135 Asset: "BTC", 2136 }, 2137 Type: types.TransferTypeLoss, 2138 }, 2139 { 2140 Owner: "testParty", 2141 Amount: &types.FinancialAmount{ 2142 Amount: num.UintZero().Mul(amount, num.NewUint(100)), 2143 Asset: "BTC", 2144 }, 2145 Type: types.TransferTypeWin, 2146 }, 2147 } 2148 2149 eng.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() 2150 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 2151 responses, err := eng.FinalSettlement(context.Background(), testMarketID, pos, num.UintOne(), func(string) bool { return true }) 2152 assert.NoError(t, err) 2153 assert.Equal(t, 2, len(responses)) 2154 2155 assert.Equal(t, 1, len(responses[0].Entries)) 2156 assert.Equal(t, num.NewUint(500), responses[0].Entries[0].ToAccountBalance) 2157 assert.Equal(t, num.NewUint(500), responses[0].Entries[0].ToAccountBalance) 2158 2159 assert.Equal(t, 1, len(responses[1].Entries)) 2160 assert.Equal(t, num.NewUint(500), responses[1].Entries[0].ToAccountBalance) 2161 assert.Equal(t, num.NewUint(500), responses[1].Entries[0].ToAccountBalance) 2162 } 2163 2164 func TestGetPartyMarginNoAccounts(t *testing.T) { 2165 price := num.NewUint(1000) 2166 2167 eng := getTestEngine(t) 2168 defer eng.Finish() 2169 2170 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 2171 insurancePool, err := eng.GetMarketInsurancePoolAccount(testMarketID, testMarketAsset) 2172 assert.Nil(t, err) 2173 err = eng.UpdateBalance(context.Background(), insurancePool.ID, num.UintZero().Div(price, num.NewUint(2))) 2174 assert.Nil(t, err) 2175 2176 marketPos := mtmFake{ 2177 party: "test-party", 2178 } 2179 2180 margin, err := eng.GetPartyMargin(marketPos, "BTC", testMarketID) 2181 assert.Nil(t, margin) 2182 assert.Error(t, err) 2183 } 2184 2185 func TestGetPartyMarginNoMarginAccounts(t *testing.T) { 2186 price := num.NewUint(1000) 2187 2188 eng := getTestEngine(t) 2189 defer eng.Finish() 2190 2191 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 2192 insurancePool, err := eng.GetMarketInsurancePoolAccount(testMarketID, testMarketAsset) 2193 assert.Nil(t, err) 2194 err = eng.UpdateBalance(context.Background(), insurancePool.ID, num.UintZero().Div(price, num.NewUint(2))) 2195 assert.Nil(t, err) 2196 2197 eng.broker.EXPECT().Send(gomock.Any()).Times(2) 2198 _, _ = eng.CreatePartyGeneralAccount(context.Background(), "test-party", testMarketAsset) 2199 2200 marketPos := mtmFake{ 2201 party: "test-party", 2202 } 2203 2204 margin, err := eng.GetPartyMargin(marketPos, "BTC", testMarketID) 2205 assert.Nil(t, margin) 2206 assert.Error(t, err) 2207 } 2208 2209 func TestGetPartyMarginEmpty(t *testing.T) { 2210 price := num.NewUint(1000) 2211 2212 eng := getTestEngine(t) 2213 defer eng.Finish() 2214 2215 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 2216 insurancePool, err := eng.GetMarketInsurancePoolAccount(testMarketID, testMarketAsset) 2217 assert.Nil(t, err) 2218 err = eng.UpdateBalance(context.Background(), insurancePool.ID, num.UintZero().Div(price, num.NewUint(2))) 2219 assert.Nil(t, err) 2220 2221 eng.broker.EXPECT().Send(gomock.Any()).Times(3) 2222 _, _ = eng.CreatePartyGeneralAccount(context.Background(), "test-party", testMarketAsset) 2223 _, err = eng.CreatePartyMarginAccount(context.Background(), "test-party", testMarketID, testMarketAsset) 2224 require.NoError(t, err) 2225 2226 marketPos := mtmFake{ 2227 party: "test-party", 2228 } 2229 2230 margin, err := eng.GetPartyMargin(marketPos, "BTC", testMarketID) 2231 assert.NotNil(t, margin) 2232 assert.Equal(t, margin.MarginBalance(), num.UintZero()) 2233 assert.Equal(t, margin.GeneralBalance(), num.UintZero()) 2234 assert.NoError(t, err) 2235 } 2236 2237 func TestMTMLossSocializationUnderflow(t *testing.T) { 2238 eng := getTestEngine(t) 2239 defer eng.Finish() 2240 lossParty1 := "lossparty1" 2241 winParty1 := "winparty1" 2242 winParty2 := "winparty2" 2243 winParty3 := "winparty3" 2244 2245 // create parties 2246 eng.broker.EXPECT().Send(gomock.Any()).Times(13) 2247 _, _ = eng.CreatePartyGeneralAccount(context.Background(), lossParty1, testMarketAsset) 2248 margin, err := eng.CreatePartyMarginAccount(context.Background(), lossParty1, testMarketID, testMarketAsset) 2249 eng.IncrementBalance(context.Background(), margin, num.NewUint(2)) 2250 assert.Nil(t, err) 2251 _, _ = eng.CreatePartyGeneralAccount(context.Background(), winParty1, testMarketAsset) 2252 _, err = eng.CreatePartyMarginAccount(context.Background(), winParty1, testMarketID, testMarketAsset) 2253 assert.Nil(t, err) 2254 _, _ = eng.CreatePartyGeneralAccount(context.Background(), winParty2, testMarketAsset) 2255 _, err = eng.CreatePartyMarginAccount(context.Background(), winParty2, testMarketID, testMarketAsset) 2256 assert.Nil(t, err) 2257 _, _ = eng.CreatePartyGeneralAccount(context.Background(), winParty3, testMarketAsset) 2258 _, err = eng.CreatePartyMarginAccount(context.Background(), winParty3, testMarketID, testMarketAsset) 2259 assert.Nil(t, err) 2260 2261 // 1 party loses 3, 3 parties win 1, losing party only has a balance of 2 available 2262 pos := []*types.Transfer{ 2263 { 2264 Owner: lossParty1, 2265 Amount: &types.FinancialAmount{ 2266 Amount: num.NewUint(3), 2267 Asset: testMarketAsset, 2268 }, 2269 Type: types.TransferTypeMTMLoss, 2270 }, 2271 { 2272 Owner: winParty3, 2273 Amount: &types.FinancialAmount{ 2274 Amount: num.NewUint(1), 2275 Asset: testMarketAsset, 2276 }, 2277 Type: types.TransferTypeMTMWin, 2278 }, 2279 { 2280 Owner: winParty1, 2281 Amount: &types.FinancialAmount{ 2282 Amount: num.NewUint(1), 2283 Asset: testMarketAsset, 2284 }, 2285 Type: types.TransferTypeMTMWin, 2286 }, 2287 { 2288 Owner: winParty2, 2289 Amount: &types.FinancialAmount{ 2290 Amount: num.NewUint(1), 2291 Asset: testMarketAsset, 2292 }, 2293 Type: types.TransferTypeMTMWin, 2294 }, 2295 } 2296 2297 eng.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() 2298 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes().Do(func(evt events.Event) { 2299 ae, ok := evt.(accEvt) 2300 assert.True(t, ok) 2301 acc := ae.Account() 2302 if acc.Owner == winParty3 && acc.Type == types.AccountTypeMargin { 2303 assert.Equal(t, 0, stringToInt(acc.Balance)) 2304 } 2305 if acc.Owner == winParty1 && acc.Type == types.AccountTypeMargin { 2306 assert.Equal(t, 0, stringToInt(acc.Balance)) 2307 } 2308 if acc.Owner == winParty2 && acc.Type == types.AccountTypeMargin { 2309 assert.Equal(t, 2, stringToInt(acc.Balance)) 2310 } 2311 }) 2312 transfers := eng.getTestMTMTransfer(pos) 2313 evts, raw, err := eng.MarkToMarket(context.Background(), testMarketID, transfers, "BTC", func(string) bool { return true }) 2314 assert.NoError(t, err) 2315 assert.Equal(t, 4, len(raw)) 2316 assert.NotEmpty(t, evts) 2317 2318 assert.Equal(t, 1, len(raw[0].Entries)) 2319 assert.Equal(t, num.NewUint(2), raw[0].Entries[0].ToAccountBalance) 2320 assert.Equal(t, num.NewUint(2), raw[0].Entries[0].ToAccountBalance) 2321 2322 assert.Equal(t, 1, len(raw[1].Entries)) 2323 assert.Equal(t, num.NewUint(0), raw[1].Entries[0].ToAccountBalance) 2324 assert.Equal(t, num.NewUint(0), raw[1].Entries[0].ToAccountBalance) 2325 2326 assert.Equal(t, 1, len(raw[2].Entries)) 2327 assert.Equal(t, num.NewUint(0), raw[2].Entries[0].ToAccountBalance) 2328 assert.Equal(t, num.NewUint(0), raw[2].Entries[0].ToAccountBalance) 2329 2330 assert.Equal(t, 1, len(raw[3].Entries)) 2331 assert.Equal(t, num.NewUint(2), raw[3].Entries[0].ToAccountBalance) 2332 assert.Equal(t, num.NewUint(2), raw[3].Entries[0].ToAccountBalance) 2333 } 2334 2335 func TestMTMLossSocialization(t *testing.T) { 2336 eng := getTestEngine(t) 2337 defer eng.Finish() 2338 lossParty1 := "lossparty1" 2339 lossParty2 := "lossparty2" 2340 winParty1 := "winparty1" 2341 winParty2 := "winparty2" 2342 2343 // create parties 2344 eng.broker.EXPECT().Send(gomock.Any()).Times(18) 2345 _, _ = eng.CreatePartyGeneralAccount(context.Background(), lossParty1, testMarketAsset) 2346 margin, err := eng.CreatePartyMarginAccount(context.Background(), lossParty1, testMarketID, testMarketAsset) 2347 eng.IncrementBalance(context.Background(), margin, num.NewUint(500)) 2348 assert.Nil(t, err) 2349 _, _ = eng.CreatePartyGeneralAccount(context.Background(), lossParty2, testMarketAsset) 2350 margin, err = eng.CreatePartyMarginAccount(context.Background(), lossParty2, testMarketID, testMarketAsset) 2351 eng.IncrementBalance(context.Background(), margin, num.NewUint(1100)) 2352 assert.Nil(t, err) 2353 _, _ = eng.CreatePartyGeneralAccount(context.Background(), winParty1, testMarketAsset) 2354 _, err = eng.CreatePartyMarginAccount(context.Background(), winParty1, testMarketID, testMarketAsset) 2355 // eng.IncrementBalance(context.Background(), margin, 0) 2356 assert.Nil(t, err) 2357 _, _ = eng.CreatePartyGeneralAccount(context.Background(), winParty2, testMarketAsset) 2358 _, err = eng.CreatePartyMarginAccount(context.Background(), winParty2, testMarketID, testMarketAsset) 2359 // eng.IncrementBalance(context.Background(), margin, 700) 2360 assert.Nil(t, err) 2361 2362 pos := []*types.Transfer{ 2363 { 2364 Owner: lossParty1, 2365 Amount: &types.FinancialAmount{ 2366 Amount: num.NewUint(700), 2367 Asset: testMarketAsset, 2368 }, 2369 Type: types.TransferTypeMTMLoss, 2370 }, 2371 { 2372 Owner: lossParty2, 2373 Amount: &types.FinancialAmount{ 2374 Amount: num.NewUint(1400), 2375 Asset: testMarketAsset, 2376 }, 2377 Type: types.TransferTypeMTMLoss, 2378 }, 2379 { 2380 Owner: winParty1, 2381 Amount: &types.FinancialAmount{ 2382 Amount: num.NewUint(1400), 2383 Asset: testMarketAsset, 2384 }, 2385 Type: types.TransferTypeMTMWin, 2386 }, 2387 { 2388 Owner: winParty2, 2389 Amount: &types.FinancialAmount{ 2390 Amount: num.NewUint(700), 2391 Asset: testMarketAsset, 2392 }, 2393 Type: types.TransferTypeMTMWin, 2394 }, 2395 } 2396 2397 eng.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() 2398 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes().Do(func(evt events.Event) { 2399 ae, ok := evt.(accEvt) 2400 assert.True(t, ok) 2401 acc := ae.Account() 2402 if acc.Owner == winParty1 && acc.Type == types.AccountTypeMargin { 2403 assert.Equal(t, 1066, stringToInt(acc.Balance)) 2404 } 2405 if acc.Owner == winParty2 && acc.Type == types.AccountTypeMargin { 2406 assert.Equal(t, 534, stringToInt(acc.Balance)) 2407 } 2408 }) 2409 transfers := eng.getTestMTMTransfer(pos) 2410 evts, raw, err := eng.MarkToMarket(context.Background(), testMarketID, transfers, "BTC", func(string) bool { return true }) 2411 assert.NoError(t, err) 2412 assert.Equal(t, 4, len(raw)) 2413 assert.NotEmpty(t, evts) 2414 2415 assert.Equal(t, 1, len(raw[0].Entries)) 2416 assert.Equal(t, num.NewUint(500), raw[0].Entries[0].ToAccountBalance) 2417 assert.Equal(t, num.NewUint(500), raw[0].Entries[0].ToAccountBalance) 2418 2419 assert.Equal(t, 1, len(raw[1].Entries)) 2420 assert.Equal(t, num.NewUint(1600), raw[1].Entries[0].ToAccountBalance) 2421 assert.Equal(t, num.NewUint(1600), raw[1].Entries[0].ToAccountBalance) 2422 2423 assert.Equal(t, 1, len(raw[2].Entries)) 2424 assert.Equal(t, num.NewUint(1066), raw[2].Entries[0].ToAccountBalance) 2425 assert.Equal(t, num.NewUint(1066), raw[2].Entries[0].ToAccountBalance) 2426 2427 assert.Equal(t, 1, len(raw[3].Entries)) 2428 assert.Equal(t, num.NewUint(534), raw[3].Entries[0].ToAccountBalance) 2429 assert.Equal(t, num.NewUint(534), raw[3].Entries[0].ToAccountBalance) 2430 } 2431 2432 func testMarginUpdateOnOrderOK(t *testing.T) { 2433 eng := getTestEngine(t) 2434 defer eng.Finish() 2435 party := "okparty" 2436 2437 // create parties 2438 eng.broker.EXPECT().Send(gomock.Any()).Times(4) 2439 acc, _ := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 2440 eng.IncrementBalance(context.Background(), acc, num.NewUint(500)) 2441 _, err := eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 2442 assert.Nil(t, err) 2443 2444 evt := riskFake{ 2445 asset: testMarketAsset, 2446 amount: num.NewUint(100), 2447 transfer: &types.Transfer{ 2448 Owner: party, 2449 Amount: &types.FinancialAmount{ 2450 Amount: num.NewUint(100), 2451 Asset: testMarketAsset, 2452 }, 2453 MinAmount: num.NewUint(100), 2454 Type: types.TransferTypeMarginLow, 2455 }, 2456 } 2457 2458 eng.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() 2459 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes().Do(func(evt events.Event) { 2460 ae, ok := evt.(accEvt) 2461 assert.True(t, ok) 2462 acc := ae.Account() 2463 if acc.Owner == party && acc.Type == types.AccountTypeMargin { 2464 assert.Equal(t, stringToInt(acc.Balance), 100) 2465 } 2466 }) 2467 resp, closed, err := eng.MarginUpdateOnOrder(context.Background(), testMarketID, evt) 2468 assert.Nil(t, err) 2469 assert.Nil(t, closed) 2470 assert.NotNil(t, resp) 2471 2472 assert.Equal(t, 1, len(resp.Entries)) 2473 assert.Equal(t, num.NewUint(100), resp.Entries[0].ToAccountBalance) 2474 assert.Equal(t, num.NewUint(100), resp.Entries[0].ToAccountBalance) 2475 } 2476 2477 func testMarginUpdateOnOrderOKNotShortFallWithBondAccount(t *testing.T) { 2478 eng := getTestEngine(t) 2479 defer eng.Finish() 2480 party := "okparty" 2481 2482 // create parties 2483 eng.broker.EXPECT().Send(gomock.Any()).Times(6) 2484 acc, _ := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 2485 eng.IncrementBalance(context.Background(), acc, num.NewUint(500)) 2486 bondacc, _ := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 2487 eng.IncrementBalance(context.Background(), bondacc, num.NewUint(500)) 2488 _, err := eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 2489 assert.Nil(t, err) 2490 2491 evt := riskFake{ 2492 asset: testMarketAsset, 2493 amount: num.NewUint(100), 2494 transfer: &types.Transfer{ 2495 Owner: party, 2496 Amount: &types.FinancialAmount{ 2497 Amount: num.NewUint(100), 2498 Asset: testMarketAsset, 2499 }, 2500 MinAmount: num.NewUint(100), 2501 Type: types.TransferTypeMarginLow, 2502 }, 2503 } 2504 2505 eng.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() 2506 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes().Do(func(evt events.Event) { 2507 ae, ok := evt.(accEvt) 2508 assert.True(t, ok) 2509 acc := ae.Account() 2510 if acc.Owner == party && acc.Type == types.AccountTypeMargin { 2511 assert.Equal(t, stringToInt(acc.Balance), 100) 2512 } 2513 }) 2514 resp, closed, err := eng.MarginUpdateOnOrder(context.Background(), testMarketID, evt) 2515 assert.Nil(t, err) 2516 assert.Nil(t, closed) 2517 assert.NotNil(t, resp) 2518 2519 assert.Equal(t, 1, len(resp.Entries)) 2520 assert.Equal(t, num.NewUint(100), resp.Entries[0].ToAccountBalance) 2521 assert.Equal(t, num.NewUint(100), resp.Entries[0].ToAccountBalance) 2522 } 2523 2524 func testMarginUpdateOnOrderOKUseBondAccount(t *testing.T) { 2525 eng := getTestEngine(t) 2526 defer eng.Finish() 2527 party := "okparty" 2528 2529 // create parties 2530 eng.broker.EXPECT().Send(gomock.Any()).Times(6) 2531 genaccID, _ := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 2532 eng.IncrementBalance(context.Background(), genaccID, num.UintZero()) 2533 bondAccID, _ := eng.CreatePartyBondAccount(context.Background(), party, testMarketID, testMarketAsset) 2534 eng.IncrementBalance(context.Background(), bondAccID, num.NewUint(500)) 2535 _, err := eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 2536 assert.Nil(t, err) 2537 2538 evt := riskFake{ 2539 asset: testMarketAsset, 2540 amount: num.NewUint(100), 2541 transfer: &types.Transfer{ 2542 Owner: party, 2543 Amount: &types.FinancialAmount{ 2544 Amount: num.NewUint(100), 2545 Asset: testMarketAsset, 2546 }, 2547 MinAmount: num.NewUint(100), 2548 Type: types.TransferTypeMarginLow, 2549 }, 2550 } 2551 2552 eng.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() 2553 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes().Do(func(evt events.Event) { 2554 ae, ok := evt.(accEvt) 2555 assert.True(t, ok) 2556 acc := ae.Account() 2557 if acc.Owner == party && acc.Type == types.AccountTypeMargin { 2558 assert.Equal(t, stringToInt(acc.Balance), 100) 2559 } 2560 }) 2561 resp, closed, err := eng.MarginUpdateOnOrder(context.Background(), testMarketID, evt) 2562 assert.Nil(t, err) 2563 assert.NotNil(t, closed) 2564 assert.NotNil(t, resp) 2565 2566 assert.Equal(t, closed.MarginShortFall(), num.NewUint(100)) 2567 2568 gacc, err := eng.GetAccountByID(genaccID) 2569 assert.NoError(t, err) 2570 assert.Equal(t, num.UintZero(), gacc.Balance) 2571 bondAcc, err := eng.GetAccountByID(bondAccID) 2572 assert.NoError(t, err) 2573 assert.Equal(t, num.NewUint(400), bondAcc.Balance) 2574 2575 assert.Equal(t, 1, len(resp.Entries)) 2576 assert.Equal(t, num.NewUint(100), resp.Entries[0].ToAccountBalance) 2577 assert.Equal(t, num.NewUint(100), resp.Entries[0].ToAccountBalance) 2578 } 2579 2580 func testMarginUpdateOnOrderOKUseBondAndGeneralAccounts(t *testing.T) { 2581 eng := getTestEngine(t) 2582 defer eng.Finish() 2583 party := "okparty" 2584 2585 // create parties 2586 eng.broker.EXPECT().Send(gomock.Any()).Times(6) 2587 genaccID, _ := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 2588 eng.IncrementBalance(context.Background(), genaccID, num.NewUint(70)) 2589 bondAccID, _ := eng.CreatePartyBondAccount(context.Background(), party, testMarketID, testMarketAsset) 2590 eng.IncrementBalance(context.Background(), bondAccID, num.NewUint(500)) 2591 marginAccID, err := eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 2592 assert.Nil(t, err) 2593 2594 evt := riskFake{ 2595 asset: testMarketAsset, 2596 amount: num.NewUint(100), 2597 transfer: &types.Transfer{ 2598 Owner: party, 2599 Amount: &types.FinancialAmount{ 2600 Amount: num.NewUint(100), 2601 Asset: testMarketAsset, 2602 }, 2603 MinAmount: num.NewUint(100), 2604 Type: types.TransferTypeMarginLow, 2605 }, 2606 } 2607 2608 eng.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() 2609 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes().Do(func(evt events.Event) { 2610 ae, ok := evt.(accEvt) 2611 assert.True(t, ok) 2612 acc := ae.Account() 2613 // first call is to be updated to 70 with bond accoutns funds 2614 // then to 100 with general account funds 2615 if acc.Owner == party && acc.Type == types.AccountTypeMargin { 2616 assert.True(t, stringToInt(acc.Balance) == 70 || stringToInt(acc.Balance) == 100) 2617 } 2618 }) 2619 2620 resp, closed, err := eng.MarginUpdateOnOrder(context.Background(), testMarketID, evt) 2621 assert.Nil(t, err) 2622 assert.NotNil(t, closed) 2623 assert.NotNil(t, resp) 2624 2625 // we toped up only 70 in the bond account 2626 // but required 100 so we should pick 30 in the general account as well. 2627 2628 // check shortfall 2629 assert.Equal(t, closed.MarginShortFall(), num.NewUint(30)) 2630 2631 gacc, err := eng.GetAccountByID(genaccID) 2632 assert.NoError(t, err) 2633 assert.Equal(t, num.UintZero(), gacc.Balance) 2634 bondAcc, err := eng.GetAccountByID(bondAccID) 2635 assert.NoError(t, err) 2636 assert.Equal(t, num.NewUint(470), bondAcc.Balance) 2637 marginAcc, err := eng.GetAccountByID(marginAccID) 2638 assert.NoError(t, err) 2639 assert.Equal(t, num.NewUint(100), marginAcc.Balance) 2640 } 2641 2642 func testMarginUpdateOnOrderOKThenRollback(t *testing.T) { 2643 eng := getTestEngine(t) 2644 defer eng.Finish() 2645 party := "okparty" 2646 2647 // create parties 2648 eng.broker.EXPECT().Send(gomock.Any()).Times(4) 2649 acc, _ := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 2650 eng.IncrementBalance(context.Background(), acc, num.NewUint(500)) 2651 _, err := eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 2652 assert.Nil(t, err) 2653 2654 evt := riskFake{ 2655 asset: testMarketAsset, 2656 amount: num.NewUint(100), 2657 transfer: &types.Transfer{ 2658 Owner: party, 2659 Amount: &types.FinancialAmount{ 2660 Amount: num.NewUint(100), 2661 Asset: testMarketAsset, 2662 }, 2663 MinAmount: num.NewUint(100), 2664 Type: types.TransferTypeMarginLow, 2665 }, 2666 } 2667 2668 eng.broker.EXPECT().Send(gomock.Any()).Times(2).Do(func(evt events.Event) { 2669 ae, ok := evt.(accEvt) 2670 assert.True(t, ok) 2671 acc := ae.Account() 2672 if acc.Owner == party && acc.Type == types.AccountTypeMargin { 2673 assert.Equal(t, stringToInt(acc.Balance), 100) 2674 } 2675 if acc.Owner == party && acc.Type == types.AccountTypeGeneral { 2676 assert.Equal(t, stringToInt(acc.Balance), 400) 2677 } 2678 }) 2679 resp, closed, err := eng.MarginUpdateOnOrder(context.Background(), testMarketID, evt) 2680 assert.Nil(t, err) 2681 assert.Nil(t, closed) 2682 assert.NotNil(t, resp) 2683 2684 // then rollback 2685 rollback := &types.Transfer{ 2686 Owner: party, 2687 Amount: &types.FinancialAmount{ 2688 Amount: num.NewUint(100), 2689 Asset: testMarketAsset, 2690 }, 2691 MinAmount: num.NewUint(100), 2692 Type: types.TransferTypeMarginLow, 2693 } 2694 2695 eng.broker.EXPECT().Send(gomock.Any()).Times(2).Do(func(evt events.Event) { 2696 ae, ok := evt.(accEvt) 2697 assert.True(t, ok) 2698 acc := ae.Account() 2699 if acc.Owner == party && acc.Type == types.AccountTypeMargin { 2700 assert.Equal(t, stringToInt(acc.Balance), 0) 2701 } 2702 if acc.Owner == party && acc.Type == types.AccountTypeGeneral { 2703 assert.Equal(t, stringToInt(acc.Balance), 500) 2704 } 2705 }) 2706 resp, err = eng.RollbackMarginUpdateOnOrder(context.Background(), testMarketID, testMarketAsset, rollback) 2707 assert.Nil(t, err) 2708 assert.NotNil(t, resp) 2709 2710 assert.Equal(t, 1, len(resp.Entries)) 2711 assert.Equal(t, num.NewUint(500), resp.Entries[0].ToAccountBalance) 2712 assert.Equal(t, num.NewUint(500), resp.Entries[0].ToAccountBalance) 2713 } 2714 2715 func testMarginUpdateOnOrderFail(t *testing.T) { 2716 eng := getTestEngine(t) 2717 defer eng.Finish() 2718 party := "okparty" 2719 2720 // create parties 2721 eng.broker.EXPECT().Send(gomock.Any()).Times(3) 2722 _, _ = eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 2723 _, err := eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 2724 assert.Nil(t, err) 2725 2726 evt := riskFake{ 2727 asset: testMarketAsset, 2728 amount: num.NewUint(100000), 2729 transfer: &types.Transfer{ 2730 Owner: party, 2731 Amount: &types.FinancialAmount{ 2732 Amount: num.NewUint(100000), 2733 Asset: testMarketAsset, 2734 }, 2735 MinAmount: num.NewUint(100000), 2736 Type: types.TransferTypeMarginLow, 2737 }, 2738 } 2739 2740 resp, closed, err := eng.MarginUpdateOnOrder(context.Background(), testMarketID, evt) 2741 assert.NotNil(t, err) 2742 assert.Error(t, err, collateral.ErrMinAmountNotReached.Error()) 2743 assert.NotNil(t, closed) 2744 assert.Nil(t, resp) 2745 } 2746 2747 func TestMarginUpdates(t *testing.T) { 2748 eng := getTestEngine(t) 2749 defer eng.Finish() 2750 party := "okparty" 2751 2752 // create parties 2753 eng.broker.EXPECT().Send(gomock.Any()).Times(6) 2754 acc, _ := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 2755 eng.IncrementBalance(context.Background(), acc, num.NewUint(500)) 2756 _, err := eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 2757 assert.Nil(t, err) 2758 2759 list := make([]events.Risk, 1) 2760 2761 list[0] = riskFake{ 2762 asset: testMarketAsset, 2763 amount: num.NewUint(100), 2764 transfer: &types.Transfer{ 2765 Owner: party, 2766 Amount: &types.FinancialAmount{ 2767 Amount: num.NewUint(100), 2768 Asset: testMarketAsset, 2769 }, 2770 MinAmount: num.NewUint(100), 2771 Type: types.TransferTypeMarginLow, 2772 }, 2773 } 2774 2775 resp, margin, _, err := eng.MarginUpdate(context.Background(), testMarketID, list) 2776 assert.Nil(t, err) 2777 assert.Equal(t, len(margin), 0) 2778 assert.Equal(t, len(resp), 1) 2779 assert.Equal(t, resp[0].Entries[0].Amount, num.NewUint(100)) 2780 2781 assert.Equal(t, 1, len(resp[0].Entries)) 2782 assert.Equal(t, num.NewUint(100), resp[0].Entries[0].ToAccountBalance) 2783 assert.Equal(t, num.NewUint(100), resp[0].Entries[0].ToAccountBalance) 2784 } 2785 2786 func TestClearMarket(t *testing.T) { 2787 eng := getTestEngine(t) 2788 defer eng.Finish() 2789 party := "okparty" 2790 2791 // create parties 2792 eng.broker.EXPECT().Send(gomock.Any()).Times(12) 2793 2794 eng.IncrementBalance(context.Background(), eng.marketInsuranceID, num.NewUint(1000)) 2795 2796 _, err := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 2797 assert.Nil(t, err) 2798 acc, err := eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 2799 eng.IncrementBalance(context.Background(), acc, num.NewUint(500)) 2800 assert.Nil(t, err) 2801 2802 // increment the balance on the lpFee account so we can check it gets cleared 2803 liqAcc, _ := eng.GetMarketLiquidityFeeAccount(testMarketID, testMarketAsset) 2804 eng.IncrementBalance(context.Background(), liqAcc.ID, num.NewUint(250)) 2805 2806 parties := []string{party} 2807 2808 responses, err := eng.ClearMarket(context.Background(), testMarketID, testMarketAsset, parties, false) 2809 2810 assert.Nil(t, err) 2811 assert.Equal(t, 3, len(responses)) 2812 2813 // this will be from the margin account to the general account 2814 assert.Equal(t, 1, len(responses[0].Entries)) 2815 entry := responses[0].Entries[0] 2816 assert.Equal(t, types.AccountTypeMargin, entry.FromAccount.Type) 2817 assert.Equal(t, types.AccountTypeGeneral, entry.ToAccount.Type) 2818 assert.Equal(t, num.NewUint(0), entry.FromAccountBalance) 2819 assert.Equal(t, num.NewUint(500), entry.ToAccountBalance) 2820 assert.Equal(t, num.NewUint(500), entry.Amount) 2821 2822 // This will be liquidity fees being cleared into the insurance account 2823 assert.Equal(t, 1, len(responses[1].Entries)) 2824 entry = responses[1].Entries[0] 2825 assert.Equal(t, types.AccountTypeFeesLiquidity, entry.FromAccount.Type) 2826 assert.Equal(t, types.AccountTypeInsurance, entry.ToAccount.Type) 2827 assert.Equal(t, num.NewUint(0), entry.FromAccountBalance) 2828 assert.Equal(t, num.NewUint(1250), entry.ToAccountBalance) 2829 assert.Equal(t, num.NewUint(250), entry.Amount) 2830 2831 // This will be the insurance account going into the global insurance pool 2832 entry = responses[2].Entries[0] 2833 assert.Equal(t, types.AccountTypeInsurance, entry.FromAccount.Type) 2834 assert.Equal(t, types.AccountTypeGlobalInsurance, entry.ToAccount.Type) 2835 assert.Equal(t, num.NewUint(0), entry.FromAccountBalance) 2836 assert.Equal(t, num.NewUint(1250), entry.ToAccountBalance) 2837 assert.Equal(t, num.NewUint(1250), entry.Amount) 2838 } 2839 2840 func TestClearMarketNoMargin(t *testing.T) { 2841 eng := getTestEngine(t) 2842 defer eng.Finish() 2843 party := "okparty" 2844 2845 // create parties 2846 eng.broker.EXPECT().Send(gomock.Any()).Times(3) 2847 acc, _ := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 2848 eng.IncrementBalance(context.Background(), acc, num.NewUint(500)) 2849 2850 parties := []string{party} 2851 2852 responses, err := eng.ClearMarket(context.Background(), testMarketID, testMarketAsset, parties, false) 2853 2854 // we expect no ledger movements as all accounts to clear were empty 2855 assert.NoError(t, err) 2856 assert.Equal(t, len(responses), 0) 2857 } 2858 2859 func TestRewardDepositOK(t *testing.T) { 2860 eng := getTestEngine(t) 2861 defer eng.Finish() 2862 ctx := context.Background() 2863 2864 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 2865 2866 // Attempt to deposit collateral that should go into the global asset reward account 2867 _, err := eng.Deposit(ctx, rewardsID, testMarketAsset, num.NewUint(100)) 2868 assert.NoError(t, err) 2869 2870 rewardAcct, err := eng.GetGlobalRewardAccount(testMarketAsset) 2871 assert.NoError(t, err) 2872 assert.Equal(t, num.NewUint(100), rewardAcct.Balance) 2873 2874 // Add 400 more to the reward account 2875 _, err = eng.Deposit(ctx, rewardsID, testMarketAsset, num.NewUint(400)) 2876 assert.NoError(t, err) 2877 2878 rewardAcct, err = eng.GetGlobalRewardAccount(testMarketAsset) 2879 assert.NoError(t, err) 2880 assert.Equal(t, num.NewUint(500), rewardAcct.Balance) 2881 } 2882 2883 func TestNonRewardDepositOK(t *testing.T) { 2884 eng := getTestEngine(t) 2885 defer eng.Finish() 2886 ctx := context.Background() 2887 2888 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 2889 2890 // Attempt to deposit collateral that should go into the global asset reward account 2891 _, err := eng.Deposit(ctx, "OtherParty", testMarketAsset, num.NewUint(100)) 2892 assert.NoError(t, err) 2893 } 2894 2895 func TestRewardDepositBadAssetOK(t *testing.T) { 2896 eng := getTestEngine(t) 2897 defer eng.Finish() 2898 ctx := context.Background() 2899 testAsset2 := "VEGA" 2900 2901 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 2902 2903 // Now try a different asset 2904 _, err := eng.Deposit(ctx, rewardsID, testAsset2, num.NewUint(333)) 2905 assert.Error(t, err) 2906 } 2907 2908 func TestWithdrawalOK(t *testing.T) { 2909 eng := getTestEngine(t) 2910 defer eng.Finish() 2911 party := "okparty" 2912 2913 // create parties 2914 eng.broker.EXPECT().Send(gomock.Any()).Times(4) 2915 acc, _ := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 2916 eng.IncrementBalance(context.Background(), acc, num.NewUint(500)) 2917 _, err := eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 2918 assert.Nil(t, err) 2919 2920 eng.broker.EXPECT().Send(gomock.Any()).Times(1).Do(func(evt events.Event) { 2921 ae, ok := evt.(accEvt) 2922 assert.True(t, ok) 2923 acc := ae.Account() 2924 if acc.Type == types.AccountTypeGeneral { 2925 assert.Equal(t, 400, stringToInt(acc.Balance)) 2926 } else { 2927 t.FailNow() 2928 } 2929 }) 2930 2931 lm, err := eng.Withdraw(context.Background(), party, testMarketAsset, num.NewUint(100)) 2932 assert.Nil(t, err) 2933 2934 assert.Equal(t, 1, len(lm.Entries)) 2935 assert.Equal(t, num.NewUint(100), lm.Entries[0].ToAccountBalance) 2936 assert.Equal(t, num.NewUint(100), lm.Entries[0].ToAccountBalance) 2937 } 2938 2939 func TestWithdrawalExact(t *testing.T) { 2940 eng := getTestEngine(t) 2941 defer eng.Finish() 2942 party := "okparty" 2943 2944 // create parties 2945 eng.broker.EXPECT().Send(gomock.Any()).Times(5) 2946 acc, _ := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 2947 eng.IncrementBalance(context.Background(), acc, num.NewUint(500)) 2948 _, err := eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 2949 assert.Nil(t, err) 2950 2951 _, err = eng.Withdraw(context.Background(), party, testMarketAsset, num.NewUint(500)) 2952 assert.Nil(t, err) 2953 2954 accAfter, err := eng.GetPartyGeneralAccount(party, testMarketAsset) 2955 assert.NoError(t, err) 2956 assert.Equal(t, accAfter.Balance, num.UintZero()) 2957 } 2958 2959 func TestWithdrawalNotEnough(t *testing.T) { 2960 eng := getTestEngine(t) 2961 defer eng.Finish() 2962 party := "okparty" 2963 2964 // create parties 2965 eng.broker.EXPECT().Send(gomock.Any()).Times(4) 2966 acc, _ := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 2967 eng.IncrementBalance(context.Background(), acc, num.NewUint(500)) 2968 _, err := eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 2969 assert.Nil(t, err) 2970 2971 _, err = eng.Withdraw(context.Background(), party, testMarketAsset, num.NewUint(600)) 2972 assert.EqualError(t, err, collateral.ErrNotEnoughFundsToWithdraw.Error()) 2973 } 2974 2975 func TestWithdrawalInvalidAccount(t *testing.T) { 2976 eng := getTestEngine(t) 2977 defer eng.Finish() 2978 party := "okparty" 2979 2980 // create parties 2981 eng.broker.EXPECT().Send(gomock.Any()).Times(4) 2982 acc, _ := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 2983 eng.IncrementBalance(context.Background(), acc, num.NewUint(500)) 2984 _, err := eng.CreatePartyMarginAccount(context.Background(), party, testMarketID, testMarketAsset) 2985 assert.Nil(t, err) 2986 2987 _, err = eng.Withdraw(context.Background(), "invalid", testMarketAsset, num.NewUint(600)) 2988 assert.Error(t, err) 2989 } 2990 2991 func TestChangeBalance(t *testing.T) { 2992 eng := getTestEngine(t) 2993 defer eng.Finish() 2994 party := "okparty" 2995 2996 eng.broker.EXPECT().Send(gomock.Any()).Times(2) 2997 acc, _ := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) 2998 2999 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 3000 eng.IncrementBalance(context.Background(), acc, num.NewUint(500)) 3001 account, err := eng.GetAccountByID(acc) 3002 assert.NoError(t, err) 3003 assert.Equal(t, account.Balance, num.NewUint(500)) 3004 3005 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 3006 eng.IncrementBalance(context.Background(), acc, num.NewUint(250)) 3007 account, err = eng.GetAccountByID(acc) 3008 require.NoError(t, err) 3009 assert.Equal(t, account.Balance, num.NewUint(750)) 3010 3011 eng.broker.EXPECT().Send(gomock.Any()).Times(1) 3012 eng.UpdateBalance(context.Background(), acc, num.NewUint(666)) 3013 account, err = eng.GetAccountByID(acc) 3014 require.NoError(t, err) 3015 assert.Equal(t, account.Balance, num.NewUint(666)) 3016 3017 err = eng.IncrementBalance(context.Background(), "invalid", num.NewUint(200)) 3018 assert.Error(t, err, collateral.ErrAccountDoesNotExist) 3019 3020 err = eng.UpdateBalance(context.Background(), "invalid", num.NewUint(300)) 3021 assert.Error(t, err, collateral.ErrAccountDoesNotExist) 3022 } 3023 3024 func TestReloadConfig(t *testing.T) { 3025 eng := getTestEngine(t) 3026 defer eng.Finish() 3027 3028 // Check that the log level is currently `debug` 3029 assert.Equal(t, eng.Level.Level, logging.DebugLevel) 3030 3031 // Create a new config and make some changes to it 3032 newConfig := collateral.NewDefaultConfig() 3033 newConfig.Level = encoding.LogLevel{ 3034 Level: logging.InfoLevel, 3035 } 3036 eng.ReloadConf(newConfig) 3037 3038 // Verify that the log level has been changed 3039 assert.Equal(t, eng.Level.Level, logging.InfoLevel) 3040 } 3041 3042 func (e *testEngine) getTestMTMTransfer(transfers []*types.Transfer) []events.Transfer { 3043 tt := make([]events.Transfer, 0, len(transfers)) 3044 for _, t := range transfers { 3045 // Apply some limited validation here so we can filter out bad transfers 3046 if !t.Amount.Amount.IsZero() { 3047 mt := mtmFake{ 3048 t: t, 3049 party: t.Owner, 3050 } 3051 tt = append(tt, mt) 3052 } 3053 } 3054 return tt 3055 } 3056 3057 func enableGovernanceAsset(t *testing.T, eng *collateral.Engine) { 3058 t.Helper() 3059 // add the token asset 3060 tokAsset := types.Asset{ 3061 ID: "VOTE", 3062 Details: &types.AssetDetails{ 3063 Name: "VOTE", 3064 Symbol: "VOTE", 3065 Decimals: 5, 3066 Quantum: num.DecimalZero(), 3067 Source: &types.AssetDetailsBuiltinAsset{ 3068 BuiltinAsset: &types.BuiltinAsset{ 3069 MaxFaucetAmountMint: num.UintZero(), 3070 }, 3071 }, 3072 }, 3073 } 3074 err := eng.EnableAsset(context.Background(), tokAsset) 3075 assert.NoError(t, err) 3076 } 3077 3078 func getTestEngine(t *testing.T) *testEngine { 3079 t.Helper() 3080 ctrl := gomock.NewController(t) 3081 timeSvc := mocks.NewMockTimeService(ctrl) 3082 timeSvc.EXPECT().GetTimeNow().AnyTimes() 3083 3084 broker := bmocks.NewMockBroker(ctrl) 3085 conf := collateral.NewDefaultConfig() 3086 conf.Level = encoding.LogLevel{Level: logging.DebugLevel} 3087 broker.EXPECT().Send(gomock.Any()).Times(26) 3088 // system accounts created 3089 3090 eng := collateral.New(logging.NewTestLogger(), conf, timeSvc, broker) 3091 eng.OnBalanceSnapshotFrequencyUpdated(context.Background(), 5*time.Second) 3092 enableGovernanceAsset(t, eng) 3093 3094 // enable the assert for the tests 3095 asset := types.Asset{ 3096 ID: testMarketAsset, 3097 Details: &types.AssetDetails{ 3098 Symbol: testMarketAsset, 3099 Name: testMarketAsset, 3100 Decimals: 0, 3101 Quantum: num.DecimalOne(), 3102 Source: &types.AssetDetailsBuiltinAsset{ 3103 BuiltinAsset: &types.BuiltinAsset{ 3104 MaxFaucetAmountMint: num.UintZero(), 3105 }, 3106 }, 3107 }, 3108 } 3109 err := eng.EnableAsset(context.Background(), asset) 3110 assert.NoError(t, err) 3111 // ETH is added hardcoded in some places 3112 asset = types.Asset{ 3113 ID: "ETH", 3114 Details: &types.AssetDetails{ 3115 Symbol: "ETH", 3116 Name: "ETH", 3117 Decimals: 18, 3118 Quantum: num.DecimalOne(), 3119 Source: &types.AssetDetailsBuiltinAsset{ 3120 BuiltinAsset: &types.BuiltinAsset{ 3121 MaxFaucetAmountMint: num.UintZero(), 3122 }, 3123 }, 3124 }, 3125 } 3126 err = eng.EnableAsset(context.Background(), asset) 3127 assert.NoError(t, err) 3128 3129 // create market and parties used for tests 3130 insID, setID, err := eng.CreateMarketAccounts(context.Background(), testMarketID, testMarketAsset) 3131 assert.Nil(t, err) 3132 3133 return &testEngine{ 3134 Engine: eng, 3135 ctrl: ctrl, 3136 broker: broker, 3137 timeSvc: timeSvc, 3138 marketInsuranceID: insID, 3139 marketSettlementID: setID, 3140 // systemAccs: accounts, 3141 } 3142 } 3143 3144 func TestCheckLeftOverBalance(t *testing.T) { 3145 e := getTestEngine(t) 3146 defer e.Finish() 3147 3148 e.broker.EXPECT().Send(gomock.Any()).AnyTimes() 3149 3150 ctx := context.Background() 3151 marketID := crypto.RandomHash() 3152 asset := "ETH" 3153 settleAccountID, _, err := e.CreateMarketAccounts(ctx, marketID, asset) 3154 require.NoError(t, err) 3155 3156 // settle account is empty, all good, no error, no leftover ledger entry 3157 settle := &types.Account{ 3158 ID: settleAccountID, 3159 Balance: num.UintZero(), 3160 } 3161 leftoverTransfer, err := e.CheckLeftOverBalance(ctx, settle, []*types.Transfer{}, asset, num.UintOne()) 3162 require.NoError(t, err) 3163 require.Nil(t, leftoverTransfer) 3164 3165 // settle has balance greater than 1, panic 3166 settle.Balance = num.NewUint(100) 3167 require.Panics(t, func() { e.CheckLeftOverBalance(ctx, settle, []*types.Transfer{}, asset, num.UintOne()) }) 3168 3169 // settle has balance greater than 1, market factor of 10, still panic 3170 settle.Balance = num.NewUint(100) 3171 require.Panics(t, func() { e.CheckLeftOverBalance(ctx, settle, []*types.Transfer{}, asset, num.NewUint(10)) }) 3172 3173 // settle has balance greater than 1, for a market with price factor 1000 is fine 3174 settle.Balance = num.NewUint(100) 3175 leftoverTransfer, err = e.CheckLeftOverBalance(ctx, settle, []*types.Transfer{}, asset, num.NewUint(1000)) 3176 require.NoError(t, err) 3177 require.NotNil(t, leftoverTransfer) 3178 3179 // settle has balance of exactly 1, transfer balance to the reward account 3180 settle.Balance = num.NewUint(1) 3181 leftoverTransfer, err = e.CheckLeftOverBalance(ctx, settle, []*types.Transfer{}, asset, num.UintOne()) 3182 require.NoError(t, err) 3183 require.NotNil(t, leftoverTransfer) 3184 } 3185 3186 func (e *testEngine) Finish() { 3187 e.systemAccs = nil 3188 e.ctrl.Finish() 3189 } 3190 3191 type marketPositionFake struct { 3192 party string 3193 size, buy, sell int64 3194 price *num.Uint 3195 buySumProduct, sellSumProduct *num.Uint 3196 } 3197 3198 func (m marketPositionFake) AverageEntryPrice() *num.Uint { return num.UintZero() } 3199 func (m marketPositionFake) Party() string { return m.party } 3200 func (m marketPositionFake) Size() int64 { return m.size } 3201 func (m marketPositionFake) Buy() int64 { return m.buy } 3202 func (m marketPositionFake) Sell() int64 { return m.sell } 3203 func (m marketPositionFake) Price() *num.Uint { return m.price } 3204 func (m marketPositionFake) BuySumProduct() *num.Uint { return m.buySumProduct } 3205 func (m marketPositionFake) SellSumProduct() *num.Uint { return m.sellSumProduct } 3206 func (m marketPositionFake) ClearPotentials() {} 3207 3208 func (m marketPositionFake) VWBuy() *num.Uint { 3209 if m.buy == 0 { 3210 return num.UintZero() 3211 } 3212 return num.UintZero().Div(num.NewUint(uint64(m.buy)), m.buySumProduct) 3213 } 3214 3215 func (m marketPositionFake) VWSell() *num.Uint { 3216 if m.sell == 0 { 3217 return num.UintZero() 3218 } 3219 return num.UintZero().Div(num.NewUint(uint64(m.sell)), m.sellSumProduct) 3220 } 3221 3222 type mtmFake struct { 3223 t *types.Transfer 3224 party string 3225 } 3226 3227 func (m mtmFake) AverageEntryPrice() *num.Uint { return num.UintZero() } 3228 func (m mtmFake) Party() string { return m.party } 3229 func (m mtmFake) Size() int64 { return 0 } 3230 func (m mtmFake) Price() *num.Uint { return num.UintZero() } 3231 func (m mtmFake) BuySumProduct() *num.Uint { return num.UintZero() } 3232 func (m mtmFake) SellSumProduct() *num.Uint { return num.UintZero() } 3233 func (m mtmFake) VWBuy() *num.Uint { return num.UintZero() } 3234 func (m mtmFake) VWSell() *num.Uint { return num.UintZero() } 3235 func (m mtmFake) Buy() int64 { return 0 } 3236 func (m mtmFake) Sell() int64 { return 0 } 3237 func (m mtmFake) ClearPotentials() {} 3238 func (m mtmFake) Transfer() *types.Transfer { return m.t } 3239 3240 func getMTMTransfer(transfers []*types.Transfer) []events.Transfer { 3241 r := make([]events.Transfer, 0, len(transfers)) 3242 for _, t := range transfers { 3243 r = append(r, &mtmFake{ 3244 t: t, 3245 party: t.Owner, 3246 }) 3247 } 3248 return r 3249 } 3250 3251 type riskFake struct { 3252 party string 3253 size, buy, sell int64 3254 price *num.Uint 3255 buySumProduct, sellSumProduct *num.Uint 3256 vwBuy, vwSell *num.Uint 3257 margins *types.MarginLevels 3258 amount *num.Uint 3259 transfer *types.Transfer 3260 asset string 3261 marginShortFall *num.Uint 3262 } 3263 3264 func (m riskFake) AverageEntryPrice() *num.Uint { return num.UintZero() } 3265 func (m riskFake) Party() string { return m.party } 3266 func (m riskFake) Size() int64 { return m.size } 3267 func (m riskFake) Buy() int64 { return m.buy } 3268 func (m riskFake) Sell() int64 { return m.sell } 3269 func (m riskFake) Price() *num.Uint { return m.price } 3270 func (m riskFake) BuySumProduct() *num.Uint { return m.buySumProduct } 3271 func (m riskFake) SellSumProduct() *num.Uint { return m.sellSumProduct } 3272 func (m riskFake) VWBuy() *num.Uint { return m.vwBuy } 3273 func (m riskFake) VWSell() *num.Uint { return m.vwSell } 3274 func (m riskFake) ClearPotentials() {} 3275 func (m riskFake) Transfer() *types.Transfer { return m.transfer } 3276 func (m riskFake) Amount() *num.Uint { return m.amount } 3277 func (m riskFake) MarginLevels() *types.MarginLevels { return m.margins } 3278 func (m riskFake) Asset() string { return m.asset } 3279 func (m riskFake) MarketID() string { return "" } 3280 func (m riskFake) MarginBalance() *num.Uint { return num.UintZero() } 3281 func (m riskFake) GeneralBalance() *num.Uint { return num.UintZero() } 3282 func (m riskFake) GeneralAccountBalance() *num.Uint { return num.UintZero() } 3283 func (m riskFake) BondBalance() *num.Uint { return num.UintZero() } 3284 func (m riskFake) MarginShortFall() *num.Uint { return m.marginShortFall } 3285 func (m riskFake) OrderMarginBalance() *num.Uint { return num.UintZero() } 3286 3287 type transferFees struct { 3288 tfs []*types.Transfer 3289 tfa map[string]uint64 3290 } 3291 3292 func (t transferFees) Transfers() []*types.Transfer { return t.tfs } 3293 3294 func (t transferFees) TotalFeesAmountPerParty() map[string]*num.Uint { 3295 ret := make(map[string]*num.Uint, len(t.tfa)) // convert in here, so the tests are easier to read 3296 for k, v := range t.tfa { 3297 ret[k] = num.NewUint(v) 3298 } 3299 return ret 3300 } 3301 3302 func TestHash(t *testing.T) { 3303 eng := getTestEngine(t) 3304 defer eng.Finish() 3305 3306 // Create the accounts 3307 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 3308 id1, err := eng.CreatePartyGeneralAccount(context.Background(), "t1", testMarketAsset) 3309 require.NoError(t, err) 3310 3311 id2, err := eng.CreatePartyGeneralAccount(context.Background(), "t2", testMarketAsset) 3312 require.NoError(t, err) 3313 3314 _, err = eng.CreatePartyMarginAccount(context.Background(), "t1", testMarketID, testMarketAsset) 3315 require.NoError(t, err) 3316 3317 // Add balances 3318 require.NoError(t, 3319 eng.UpdateBalance(context.Background(), id1, num.NewUint(100)), 3320 ) 3321 3322 require.NoError(t, 3323 eng.UpdateBalance(context.Background(), id2, num.NewUint(500)), 3324 ) 3325 3326 hash := eng.Hash() 3327 require.Equal(t, 3328 "a5811790fdd723ba21a320dda2a16d6f9a4abbb366a4dca78449a1c96ebdf1ae", 3329 hex.EncodeToString(hash), 3330 "It should match against the known hash", 3331 ) 3332 // compute the hash 100 times for determinism verification 3333 for i := 0; i < 100; i++ { 3334 got := eng.Hash() 3335 require.Equal(t, hash, got) 3336 } 3337 } 3338 3339 func stringToInt(s string) int { 3340 i, _ := strconv.Atoi(s) 3341 return i 3342 } 3343 3344 func TestHoldingAccount(t *testing.T) { 3345 eng := getTestEngine(t) 3346 defer eng.Finish() 3347 3348 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 3349 ctx := context.Background() 3350 3351 // create the general account for the source general account 3352 id, err := eng.CreatePartyGeneralAccount(ctx, "zohar", "BTC") 3353 require.NoError(t, err) 3354 3355 // topup the source general account 3356 require.NoError(t, eng.IncrementBalance(ctx, id, num.NewUint(1000))) 3357 3358 // we're have 1000 in the general account and 0 in the holding 3359 // transferring 800 from the general account to the holding account in two transfers of 400 3360 // expect to have the holding account balance = 800 and the general account balance = 200 3361 3362 // holding account does not exist yet - it will be created 3363 le, err := eng.TransferToHoldingAccount(ctx, &types.Transfer{ 3364 Owner: "zohar", 3365 Amount: &types.FinancialAmount{ 3366 Asset: "BTC", 3367 Amount: num.NewUint(400), 3368 }, 3369 }, types.AccountTypeGeneral) 3370 require.NoError(t, err) 3371 require.Equal(t, types.AccountTypeHolding, le.Balances[0].Account.Type) 3372 require.Equal(t, num.NewUint(400), le.Balances[0].Balance) 3373 3374 // holding account does not exist yet - it will be created 3375 le, err = eng.TransferToHoldingAccount(ctx, &types.Transfer{ 3376 Owner: "zohar", 3377 Amount: &types.FinancialAmount{ 3378 Asset: "BTC", 3379 Amount: num.NewUint(400), 3380 }, 3381 }, types.AccountTypeGeneral) 3382 require.NoError(t, err) 3383 require.Equal(t, types.AccountTypeHolding, le.Balances[0].Account.Type) 3384 require.Equal(t, num.NewUint(400), le.Balances[0].Balance) 3385 3386 // check general account balance is 200 3387 z, err := eng.GetPartyGeneralAccount("zohar", "BTC") 3388 require.NoError(t, err) 3389 require.Equal(t, num.NewUint(200), z.Balance) 3390 3391 // request to release 200 from the holding account 3392 le, err = eng.ReleaseFromHoldingAccount(ctx, &types.Transfer{ 3393 Owner: "zohar", 3394 Amount: &types.FinancialAmount{ 3395 Asset: "BTC", 3396 Amount: num.NewUint(200), 3397 }, 3398 }, types.AccountTypeGeneral) 3399 require.NoError(t, err) 3400 require.Equal(t, types.AccountTypeGeneral, le.Balances[0].Account.Type) 3401 require.Equal(t, num.NewUint(200), le.Balances[0].Balance) 3402 3403 // now request to release 600 more 3404 le, err = eng.ReleaseFromHoldingAccount(ctx, &types.Transfer{ 3405 Owner: "zohar", 3406 Amount: &types.FinancialAmount{ 3407 Asset: "BTC", 3408 Amount: num.NewUint(600), 3409 }, 3410 }, types.AccountTypeGeneral) 3411 3412 require.NoError(t, err) 3413 require.Equal(t, num.UintZero(), le.Entries[0].FromAccountBalance) 3414 require.Equal(t, num.NewUint(1000), le.Entries[0].ToAccountBalance) 3415 } 3416 3417 func TestHoldingAccountNetwork(t *testing.T) { 3418 eng := getTestEngine(t) 3419 defer eng.Finish() 3420 3421 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 3422 ctx := context.Background() 3423 3424 // create the general account for the source general account 3425 id := eng.GetOrCreateBuyBackFeesAccountID(ctx, "BTC") 3426 3427 // topup the source general account 3428 require.NoError(t, eng.IncrementBalance(ctx, id, num.NewUint(1000))) 3429 3430 // we're have 1000 in the general account and 0 in the holding 3431 // transferring 800 from the general account to the holding account in two transfers of 400 3432 // expect to have the holding account balance = 800 and the general account balance = 200 3433 3434 // holding account does not exist yet - it will be created 3435 le, err := eng.TransferToHoldingAccount(ctx, &types.Transfer{ 3436 Owner: types.NetworkParty, 3437 Amount: &types.FinancialAmount{ 3438 Asset: "BTC", 3439 Amount: num.NewUint(400), 3440 }, 3441 }, types.AccountTypeBuyBackFees) 3442 require.NoError(t, err) 3443 require.Equal(t, types.AccountTypeHolding, le.Balances[0].Account.Type) 3444 require.Equal(t, num.NewUint(400), le.Balances[0].Balance) 3445 3446 // holding account does not exist yet - it will be created 3447 le, err = eng.TransferToHoldingAccount(ctx, &types.Transfer{ 3448 Owner: types.NetworkParty, 3449 Amount: &types.FinancialAmount{ 3450 Asset: "BTC", 3451 Amount: num.NewUint(400), 3452 }, 3453 }, types.AccountTypeBuyBackFees) 3454 require.NoError(t, err) 3455 require.Equal(t, types.AccountTypeHolding, le.Balances[0].Account.Type) 3456 require.Equal(t, num.NewUint(400), le.Balances[0].Balance) 3457 3458 // check general account balance is 200 3459 z, err := eng.GetSystemAccountBalance("BTC", "!", types.AccountTypeBuyBackFees) 3460 require.NoError(t, err) 3461 require.Equal(t, num.NewUint(200), z) 3462 3463 // request to release 200 from the holding account 3464 le, err = eng.ReleaseFromHoldingAccount(ctx, &types.Transfer{ 3465 Owner: types.NetworkParty, 3466 Amount: &types.FinancialAmount{ 3467 Asset: "BTC", 3468 Amount: num.NewUint(200), 3469 }, 3470 }, types.AccountTypeBuyBackFees) 3471 require.NoError(t, err) 3472 require.Equal(t, types.AccountTypeBuyBackFees, le.Balances[0].Account.Type) 3473 require.Equal(t, num.NewUint(200), le.Balances[0].Balance) 3474 3475 // now request to release 600 more 3476 le, err = eng.ReleaseFromHoldingAccount(ctx, &types.Transfer{ 3477 Owner: types.NetworkParty, 3478 Amount: &types.FinancialAmount{ 3479 Asset: "BTC", 3480 Amount: num.NewUint(600), 3481 }, 3482 }, types.AccountTypeBuyBackFees) 3483 3484 require.NoError(t, err) 3485 require.Equal(t, num.UintZero(), le.Entries[0].FromAccountBalance) 3486 require.Equal(t, num.NewUint(1000), le.Entries[0].ToAccountBalance) 3487 } 3488 3489 func TestClearSpotMarket(t *testing.T) { 3490 eng := getTestEngine(t) 3491 defer eng.Finish() 3492 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 3493 3494 // create a spot market and top up the fees account before we try to close the market 3495 err := eng.CreateSpotMarketAccounts(context.Background(), testMarketID, "BTC") 3496 require.NoError(t, err) 3497 3498 acc, err := eng.GetMarketLiquidityFeeAccount(testMarketID, "BTC") 3499 require.NoError(t, err) 3500 3501 // create and topup bond account 3502 _, err = eng.CreatePartyGeneralAccount(context.Background(), "party1", "BTC") 3503 require.NoError(t, err) 3504 bondAccID, err := eng.CreatePartyBondAccount(context.Background(), "party1", testMarketID, "BTC") 3505 require.NoError(t, err) 3506 eng.IncrementBalance(context.Background(), bondAccID, num.NewUint(10000)) 3507 3508 eng.IncrementBalance(context.Background(), acc.ID, num.NewUint(1000)) 3509 3510 _, err = eng.GetMarketMakerFeeAccount(testMarketID, "BTC") 3511 require.NoError(t, err) 3512 3513 _, err = eng.ClearSpotMarket(context.Background(), testMarketID, "BTC", []string{"party1"}) 3514 require.NoError(t, err) 3515 3516 treasury, err := eng.GetNetworkTreasuryAccount("BTC") 3517 require.NoError(t, err) 3518 require.Equal(t, num.NewUint(1000), treasury.Balance) 3519 3520 // the liquidity and makes fees should be removed at this point 3521 _, err = eng.GetMarketLiquidityFeeAccount(testMarketID, "BTC") 3522 require.Error(t, err) 3523 3524 _, err = eng.GetMarketMakerFeeAccount(testMarketID, "BTC") 3525 require.Error(t, err) 3526 3527 generalAccountBalance, err := eng.GetPartyGeneralAccount("party1", "BTC") 3528 require.NoError(t, err) 3529 require.Equal(t, num.NewUint(10000), generalAccountBalance.Balance) 3530 } 3531 3532 func TestCreateSpotMarketAccounts(t *testing.T) { 3533 eng := getTestEngine(t) 3534 defer eng.Finish() 3535 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 3536 3537 err := eng.CreateSpotMarketAccounts(context.Background(), testMarketID, "BTC") 3538 require.NoError(t, err) 3539 3540 // check that accounts were created for liquidity and maker fees 3541 _, err = eng.GetMarketLiquidityFeeAccount(testMarketID, "BTC") 3542 require.NoError(t, err) 3543 3544 _, err = eng.GetMarketMakerFeeAccount(testMarketID, "BTC") 3545 require.NoError(t, err) 3546 } 3547 3548 func TestPartyHasSufficientBalance(t *testing.T) { 3549 eng := getTestEngine(t) 3550 defer eng.Finish() 3551 3552 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 3553 3554 // first check when general account of the source does not exist 3555 err := eng.PartyHasSufficientBalance("BTC", "zohar", num.NewUint(1000), types.AccountTypeGeneral) 3556 require.Error(t, err) 3557 3558 ctx := context.Background() 3559 // create the general account for the source general account 3560 id, err := eng.CreatePartyGeneralAccount(ctx, "zohar", "BTC") 3561 require.NoError(t, err) 3562 3563 // topup the source general account 3564 require.NoError(t, eng.IncrementBalance(ctx, id, num.NewUint(1000))) 3565 3566 err = eng.PartyHasSufficientBalance("BTC", "zohar", num.NewUint(1001), types.AccountTypeGeneral) 3567 require.Error(t, err) 3568 err = eng.PartyHasSufficientBalance("BTC", "zohar", num.NewUint(1000), types.AccountTypeGeneral) 3569 require.NoError(t, err) 3570 err = eng.PartyHasSufficientBalance("BTC", "zohar", num.NewUint(900), types.AccountTypeGeneral) 3571 require.NoError(t, err) 3572 } 3573 3574 func TestNetworkPartyHasSufficientBalance(t *testing.T) { 3575 eng := getTestEngine(t) 3576 defer eng.Finish() 3577 3578 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 3579 3580 // first check when general account of the source does not exist 3581 err := eng.PartyHasSufficientBalance("BTC", types.NetworkParty, num.NewUint(1000), types.AccountTypeGeneral) 3582 require.Error(t, err) 3583 3584 ctx := context.Background() 3585 // create the buyback account for the network on btc 3586 id := eng.GetOrCreateBuyBackFeesAccountID(ctx, "BTC") 3587 3588 // topup the source general account 3589 require.NoError(t, eng.IncrementBalance(ctx, id, num.NewUint(1000))) 3590 3591 err = eng.PartyHasSufficientBalance("BTC", types.NetworkParty, num.NewUint(1001), types.AccountTypeBuyBackFees) 3592 require.Error(t, err) 3593 err = eng.PartyHasSufficientBalance("BTC", types.NetworkParty, num.NewUint(1000), types.AccountTypeBuyBackFees) 3594 require.NoError(t, err) 3595 err = eng.PartyHasSufficientBalance("BTC", types.NetworkParty, num.NewUint(900), types.AccountTypeBuyBackFees) 3596 require.NoError(t, err) 3597 } 3598 3599 func TestCreatePartyHoldingAccount(t *testing.T) { 3600 eng := getTestEngine(t) 3601 defer eng.Finish() 3602 3603 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 3604 ctx := context.Background() 3605 3606 _, err := eng.CreatePartyHoldingAccount(ctx, "BTC2", "zohar") 3607 // asset does not exist 3608 require.Error(t, err) 3609 3610 id, err := eng.CreatePartyHoldingAccount(ctx, "zohar", "BTC") 3611 require.NoError(t, err) 3612 3613 eng.IncrementBalance(ctx, id, num.NewUint(1000)) 3614 3615 // check holding account balance 3616 acc, err := eng.GetAccountByID(id) 3617 require.NoError(t, err) 3618 require.Equal(t, types.AccountTypeHolding, acc.Type) 3619 require.Equal(t, num.NewUint(1000), acc.Balance) 3620 } 3621 3622 func TestTransferSpot(t *testing.T) { 3623 eng := getTestEngine(t) 3624 defer eng.Finish() 3625 3626 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 3627 3628 ctx := context.Background() 3629 // first check when general account of the source does not exist 3630 _, err := eng.TransferSpot(ctx, "zohar", "jeremy", "BTC", num.NewUint(900), types.AccountTypeGeneral, types.AccountTypeGeneral) 3631 require.Error(t, err) 3632 3633 // create the general account for the source general account 3634 id, err := eng.CreatePartyGeneralAccount(ctx, "zohar", "BTC") 3635 require.NoError(t, err) 3636 3637 // topup the source general account 3638 require.NoError(t, eng.IncrementBalance(ctx, id, num.NewUint(1000))) 3639 3640 // transfer successfully 3641 _, err = eng.TransferSpot(ctx, "zohar", "jeremy", "BTC", num.NewUint(900), types.AccountTypeGeneral, types.AccountTypeGeneral) 3642 require.NoError(t, err) 3643 3644 // check balances 3645 z, err := eng.GetPartyGeneralAccount("zohar", "BTC") 3646 require.NoError(t, err) 3647 3648 j, err := eng.GetPartyGeneralAccount("jeremy", "BTC") 3649 require.NoError(t, err) 3650 3651 require.Equal(t, num.NewUint(100), z.Balance) 3652 require.Equal(t, num.NewUint(900), j.Balance) 3653 3654 // try to transfer more than in the account should transfer all 3655 _, err = eng.TransferSpot(ctx, "jeremy", "zohar", "BTC", num.NewUint(1000), types.AccountTypeGeneral, types.AccountTypeGeneral) 3656 require.NoError(t, err) 3657 3658 // check balances 3659 z, err = eng.GetPartyGeneralAccount("zohar", "BTC") 3660 require.NoError(t, err) 3661 3662 j, err = eng.GetPartyGeneralAccount("jeremy", "BTC") 3663 require.NoError(t, err) 3664 3665 require.Equal(t, num.NewUint(1000), z.Balance) 3666 require.Equal(t, num.UintZero(), j.Balance) 3667 } 3668 3669 func TestTransferSpotToNetworkAccount(t *testing.T) { 3670 eng := getTestEngine(t) 3671 defer eng.Finish() 3672 3673 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 3674 3675 ctx := context.Background() 3676 // first check when network account of the source does not exist 3677 _, err := eng.TransferSpot(ctx, types.NetworkParty, "zohar", "BTC", num.NewUint(900), types.AccountTypeBuyBackFees, types.AccountTypeGeneral) 3678 require.Error(t, err) 3679 3680 // create the buyback account for the source general account 3681 id := eng.GetOrCreateBuyBackFeesAccountID(ctx, "BTC") 3682 3683 // topup the source general account 3684 require.NoError(t, eng.IncrementBalance(ctx, id, num.NewUint(1000))) 3685 3686 // transfer successfully 3687 _, err = eng.TransferSpot(ctx, types.NetworkParty, "zohar", "BTC", num.NewUint(900), types.AccountTypeBuyBackFees, types.AccountTypeGeneral) 3688 require.NoError(t, err) 3689 3690 // check balances 3691 bb, err := eng.GetAccountByID(id) 3692 require.NoError(t, err) 3693 3694 j, err := eng.GetPartyGeneralAccount("zohar", "BTC") 3695 require.NoError(t, err) 3696 3697 require.Equal(t, num.NewUint(100), bb.Balance) 3698 require.Equal(t, num.NewUint(900), j.Balance) 3699 3700 // try to transfer more than in the account should transfer all 3701 _, err = eng.TransferSpot(ctx, "zohar", types.NetworkParty, "BTC", num.NewUint(1000), types.AccountTypeGeneral, types.AccountTypeBuyBackFees) 3702 require.NoError(t, err) 3703 3704 // check balances 3705 bb, err = eng.GetAccountByID(id) 3706 require.NoError(t, err) 3707 3708 j, err = eng.GetPartyGeneralAccount("zohar", "BTC") 3709 require.NoError(t, err) 3710 3711 require.Equal(t, num.NewUint(1000), bb.Balance) 3712 require.Equal(t, num.UintZero(), j.Balance) 3713 } 3714 3715 func TestBalanceSnapshot(t *testing.T) { 3716 t.Helper() 3717 ctrl := gomock.NewController(t) 3718 timeSvc := mocks.NewMockTimeService(ctrl) 3719 3720 tm := time.Now() 3721 timeSvc.EXPECT().GetTimeNow().Return(tm).Times(2) 3722 3723 broker := bmocks.NewMockBroker(ctrl) 3724 conf := collateral.NewDefaultConfig() 3725 conf.Level = encoding.LogLevel{Level: logging.DebugLevel} 3726 3727 eng := collateral.New(logging.NewTestLogger(), conf, timeSvc, broker) 3728 3729 ctx := context.Background() 3730 3731 // enable a few assets with various quantums 3732 assets := map[string]int64{ 3733 "asset1": 1, 3734 "asset2": 2, 3735 "asset3": 3, 3736 "asset4": 4, 3737 "asset5": 5, 3738 } 3739 3740 broker.EXPECT().Send(gomock.Any()).AnyTimes() 3741 3742 for k, v := range assets { 3743 require.NoError(t, eng.EnableAsset(ctx, types.Asset{ 3744 ID: k, 3745 Details: &types.AssetDetails{ 3746 Name: k, 3747 Symbol: k, 3748 Decimals: 3, 3749 Quantum: num.DecimalFromInt64(v), 3750 Source: types.AssetDetailsBuiltinAsset{}, 3751 }, 3752 })) 3753 } 3754 3755 for k, v := range assets { 3756 id, err := eng.CreatePartyGeneralAccount(ctx, "zohar", k) 3757 require.NoError(t, err) 3758 require.NoError(t, eng.IncrementBalance(ctx, id, num.NewUint(uint64(v)*10))) 3759 } 3760 3761 // trigger the balance caching 3762 eng.BeginBlock(ctx) 3763 3764 // we have 10 units of quantum for each of the 5 tokens, so expect balance to be 50 3765 require.Equal(t, "50", eng.GetPartyBalance("zohar").String()) 3766 3767 // now make a few updates to the balances 3768 3769 for k, v := range assets { 3770 id, err := eng.CreatePartyGeneralAccount(ctx, "zohar", k) 3771 require.NoError(t, err) 3772 require.NoError(t, eng.IncrementBalance(ctx, id, num.NewUint(uint64(v)*5))) 3773 } 3774 3775 eng.OnBalanceSnapshotFrequencyUpdated(ctx, 5*time.Second) 3776 3777 // we didn't move the time so no new snapshot has been taken, therefore we expect the balance to still be 50 3778 eng.BeginBlock(ctx) 3779 require.Equal(t, "50", eng.GetPartyBalance("zohar").String()) 3780 3781 // now move the time by 5 seconds and recheck - expect 75 3782 timeSvc.EXPECT().GetTimeNow().Return(tm.Add(5 * time.Second)).Times(1) 3783 eng.BeginBlock(ctx) 3784 require.Equal(t, "75", eng.GetPartyBalance("zohar").String()) 3785 3786 keys := eng.Keys() 3787 payloads := make(map[string]*types.Payload, len(keys)) 3788 data := make(map[string][]byte, len(keys)) 3789 for _, k := range keys { 3790 payloads[k] = &types.Payload{} 3791 s, _, err := eng.GetState(k) 3792 require.NoError(t, err) 3793 data[k] = s 3794 } 3795 3796 t.Helper() 3797 timeSvc2 := mocks.NewMockTimeService(ctrl) 3798 broker2 := bmocks.NewMockBroker(ctrl) 3799 broker2.EXPECT().SendBatch(gomock.Any()).AnyTimes() 3800 newEng := collateral.New(logging.NewTestLogger(), conf, timeSvc2, broker2) 3801 3802 for k, pl := range payloads { 3803 state := data[k] 3804 ptype := pl.IntoProto() 3805 require.NoError(t, proto.Unmarshal(state, ptype)) 3806 payloads[k] = types.PayloadFromProto(ptype) 3807 _, err := newEng.LoadState(ctx, payloads[k]) 3808 require.NoError(t, err) 3809 } 3810 for k, d := range data { 3811 got, _, err := newEng.GetState(k) 3812 require.NoError(t, err) 3813 require.EqualValues(t, d, got) 3814 } 3815 3816 timeSvc.EXPECT().GetTimeNow().Return(tm.Add(20 * time.Second)).Times(1) 3817 timeSvc2.EXPECT().GetTimeNow().Return(tm.Add(20 * time.Second)).Times(1) 3818 eng.BeginBlock(ctx) 3819 newEng.BeginBlock(ctx) 3820 3821 payloads = make(map[string]*types.Payload, len(keys)) 3822 data = make(map[string][]byte, len(keys)) 3823 for _, k := range keys { 3824 payloads[k] = &types.Payload{} 3825 s, _, err := eng.GetState(k) 3826 require.NoError(t, err) 3827 data[k] = s 3828 } 3829 3830 timeSvc2 = mocks.NewMockTimeService(ctrl) 3831 broker2 = bmocks.NewMockBroker(ctrl) 3832 broker2.EXPECT().SendBatch(gomock.Any()).AnyTimes() 3833 newEng = collateral.New(logging.NewTestLogger(), conf, timeSvc2, broker2) 3834 3835 for k, pl := range payloads { 3836 state := data[k] 3837 ptype := pl.IntoProto() 3838 require.NoError(t, proto.Unmarshal(state, ptype)) 3839 payloads[k] = types.PayloadFromProto(ptype) 3840 _, err := newEng.LoadState(ctx, payloads[k]) 3841 require.NoError(t, err) 3842 } 3843 for k, d := range data { 3844 got, _, err := newEng.GetState(k) 3845 require.NoError(t, err) 3846 require.EqualValues(t, d, got) 3847 } 3848 } 3849 3850 func TestEarmarking(t *testing.T) { 3851 eng := getTestEngine(t) 3852 defer eng.Finish() 3853 3854 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 3855 3856 rewardAcc, _ := eng.GetGlobalRewardAccount("ETH") 3857 3858 eng.broker.EXPECT().Send(gomock.Any()).AnyTimes() 3859 eng.IncrementBalance(context.Background(), rewardAcc.ID, num.NewUint(1000)) 3860 3861 // test non existing account id 3862 _, err := eng.EarmarkForAutomatedPurchase("nonexisting", types.AccountTypeGlobalReward, num.UintZero(), num.NewUint(10000)) 3863 require.Error(t, err) 3864 require.Equal(t, fmt.Errorf("account does not exist: !*nonexisting<"), err) 3865 3866 // require less than the min should earmark nothing 3867 _, err = eng.EarmarkForAutomatedPurchase("ETH", types.AccountTypeGlobalReward, num.NewUint(2000), num.NewUint(10000)) 3868 require.Error(t, err) 3869 require.Equal(t, fmt.Errorf("insufficient balance to earmark"), err) 3870 3871 // require to earmark more than the max should only earmark the max 3872 amt, err := eng.EarmarkForAutomatedPurchase("ETH", types.AccountTypeGlobalReward, num.NewUint(100), num.NewUint(500)) 3873 require.NoError(t, err) 3874 require.Equal(t, num.NewUint(500), amt) 3875 3876 // now we have a balance of 1000 and 500 earmarked, so 500 more can be earmarked, let set the min to 501 and see nothing gets earmarked 3877 _, err = eng.EarmarkForAutomatedPurchase("ETH", types.AccountTypeGlobalReward, num.NewUint(501), num.NewUint(1000)) 3878 require.Equal(t, fmt.Errorf("insufficient balance to earmark"), err) 3879 3880 // earmark 500 more 3881 amt, err = eng.EarmarkForAutomatedPurchase("ETH", types.AccountTypeGlobalReward, num.NewUint(500), num.NewUint(500)) 3882 require.NoError(t, err) 3883 require.Equal(t, num.NewUint(500), amt) 3884 3885 // now we unearmarked balance of the account is 0 3886 _, err = eng.EarmarkForAutomatedPurchase("ETH", types.AccountTypeGlobalReward, num.NewUint(0), num.NewUint(1)) 3887 require.Equal(t, fmt.Errorf("insufficient balance to earmark"), err) 3888 3889 // so now we have 1000 earmarked, lets start testing unearmark 3890 err = eng.UnearmarkForAutomatedPurchase("nonexisting", types.AccountTypeGlobalReward, num.NewUint(1000)) 3891 require.Equal(t, fmt.Errorf("account does not exist: !*nonexisting<"), err) 3892 3893 // lets unearmark 600 first, should be fine 3894 require.NoError(t, eng.UnearmarkForAutomatedPurchase("ETH", types.AccountTypeGlobalReward, num.NewUint(600))) 3895 3896 // we now have 400 earmarked so lets try to unearmark 401 - should panic 3897 require.Panics(t, func() { eng.UnearmarkForAutomatedPurchase("ETH", types.AccountTypeGlobalReward, num.NewUint(401)) }) 3898 3899 // finally unearmark the last 400 successfully 3900 require.NoError(t, eng.UnearmarkForAutomatedPurchase("ETH", types.AccountTypeGlobalReward, num.NewUint(400))) 3901 }