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  }