code.vegaprotocol.io/vega@v0.79.0/core/collateral/engine.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
    17  
    18  import (
    19  	"context"
    20  	"fmt"
    21  	"sort"
    22  	"sync"
    23  	"time"
    24  
    25  	"code.vegaprotocol.io/vega/core/events"
    26  	"code.vegaprotocol.io/vega/core/types"
    27  	"code.vegaprotocol.io/vega/libs/crypto"
    28  	"code.vegaprotocol.io/vega/libs/num"
    29  	"code.vegaprotocol.io/vega/logging"
    30  	"code.vegaprotocol.io/vega/protos/vega"
    31  
    32  	"github.com/pkg/errors"
    33  	"golang.org/x/exp/maps"
    34  )
    35  
    36  const (
    37  	initialAccountSize = 4096
    38  	// use weird character here, maybe non-displayable ones in the future
    39  	// if needed.
    40  	systemOwner   = "*"
    41  	noMarket      = "!"
    42  	rewardPartyID = "0000000000000000000000000000000000000000000000000000000000000000"
    43  )
    44  
    45  var (
    46  	// ErrSystemAccountsMissing signals that a system account is missing, which may means that the
    47  	// collateral engine have not been initialised properly.
    48  	ErrSystemAccountsMissing = errors.New("system accounts missing for collateral engine to work")
    49  	// ErrFeeAccountsMissing signals that a fee account is missing, which may means that the
    50  	// collateral engine have not been initialised properly.
    51  	ErrFeeAccountsMissing = errors.New("fee accounts missing for collateral engine to work")
    52  	// ErrPartyAccountsMissing signals that the accounts for this party do not exists.
    53  	ErrPartyAccountsMissing = errors.New("party accounts missing, cannot collect")
    54  	// ErrAccountDoesNotExist signals that an account par of a transfer do not exists.
    55  	ErrAccountDoesNotExist                     = errors.New("account does not exists")
    56  	ErrNoGeneralAccountWhenCreateMarginAccount = errors.New("party general account missing when trying to create a margin account")
    57  	ErrNoGeneralAccountWhenCreateBondAccount   = errors.New("party general account missing when trying to create a bond account")
    58  	ErrMinAmountNotReached                     = errors.New("unable to reach minimum amount transfer")
    59  	ErrSettlementBalanceNotZero                = errors.New("settlement balance should be zero") // E991 YOU HAVE TOO MUCH ROPE TO HANG YOURSELF
    60  	// ErrAssetAlreadyEnabled signals the given asset has already been enabled in this engine.
    61  	ErrAssetAlreadyEnabled    = errors.New("asset already enabled")
    62  	ErrAssetHasNotBeenEnabled = errors.New("asset has not been enabled")
    63  	// ErrInvalidAssetID signals that an asset id does not exists.
    64  	ErrInvalidAssetID = errors.New("invalid asset ID")
    65  	// ErrInsufficientFundsToPayFees the party do not have enough funds to pay the feeds.
    66  	ErrInsufficientFundsToPayFees = errors.New("insufficient funds to pay fees")
    67  	// ErrInvalidTransferTypeForFeeRequest an invalid transfer type was send to build a fee transfer request.
    68  	ErrInvalidTransferTypeForFeeRequest = errors.New("an invalid transfer type was send to build a fee transfer request")
    69  	// ErrNotEnoughFundsToWithdraw a party requested to withdraw more than on its general account.
    70  	ErrNotEnoughFundsToWithdraw = errors.New("not enough funds to withdraw")
    71  	// ErrInsufficientFundsInAsset is returned if the party doesn't have sufficient funds to cover their order quantity.
    72  	ErrInsufficientFundsInAsset = errors.New("insufficient funds for order")
    73  )
    74  
    75  // Broker send events
    76  // we no longer need to generate this mock here, we can use the broker/mocks package instead.
    77  type Broker interface {
    78  	Send(event events.Event)
    79  	SendBatch(events []events.Event)
    80  }
    81  
    82  // TimeService provide the time of the vega node.
    83  //
    84  //go:generate go run github.com/golang/mock/mockgen -destination mocks/time_service_mock.go -package mocks code.vegaprotocol.io/vega/core/collateral TimeService
    85  type TimeService interface {
    86  	GetTimeNow() time.Time
    87  }
    88  
    89  // Engine is handling the power of the collateral.
    90  type Engine struct {
    91  	Config
    92  	log       *logging.Logger
    93  	cfgMu     sync.Mutex
    94  	cacheLock sync.RWMutex
    95  
    96  	accs map[string]*types.Account
    97  	// map of partyID -> account ID -> account
    98  	// this is used to check if a party have any balances in
    99  	// any assets at all
   100  	partiesAccs  map[string]map[string]*types.Account
   101  	hashableAccs []*types.Account
   102  	timeService  TimeService
   103  	broker       Broker
   104  
   105  	earmarkedBalance map[string]*num.Uint
   106  
   107  	partiesAccsBalanceCache     map[string]*num.Uint
   108  	partiesAccsBalanceCacheLock sync.RWMutex
   109  
   110  	// a block cache of the total value of general + margin + order margin accounts in quantum
   111  	partyMarketCache map[string]map[string]*num.Uint
   112  	partyAssetCache  map[string]map[string]*num.Uint
   113  
   114  	idbuf []byte
   115  
   116  	// asset ID to asset
   117  	enabledAssets map[string]types.Asset
   118  	// snapshot stuff
   119  	state *accState
   120  
   121  	// vesting account recovery
   122  	// unique usage at startup from a checkpoint
   123  	// a map of party -> (string -> balance)
   124  	vesting map[string]map[string]*num.Uint
   125  
   126  	nextBalancesSnapshot     time.Time
   127  	balanceSnapshotFrequency time.Duration
   128  
   129  	// set to false when started
   130  	// we'll use it only once after an upgrade
   131  	// to make sure asset are being created
   132  	ensuredAssetAccounts bool
   133  }
   134  
   135  // New instantiates a new collateral engine.
   136  func New(log *logging.Logger, conf Config, ts TimeService, broker Broker) *Engine {
   137  	// setup logger
   138  	log = log.Named(namedLogger)
   139  	log.SetLevel(conf.Level.Get())
   140  	return &Engine{
   141  		log:                     log,
   142  		Config:                  conf,
   143  		accs:                    make(map[string]*types.Account, initialAccountSize),
   144  		partiesAccs:             map[string]map[string]*types.Account{},
   145  		hashableAccs:            []*types.Account{},
   146  		timeService:             ts,
   147  		broker:                  broker,
   148  		idbuf:                   make([]byte, 256),
   149  		enabledAssets:           map[string]types.Asset{},
   150  		state:                   newAccState(),
   151  		vesting:                 map[string]map[string]*num.Uint{},
   152  		partiesAccsBalanceCache: map[string]*num.Uint{},
   153  		nextBalancesSnapshot:    time.Time{},
   154  		partyAssetCache:         map[string]map[string]*num.Uint{},
   155  		partyMarketCache:        map[string]map[string]*num.Uint{},
   156  		earmarkedBalance:        map[string]*num.Uint{},
   157  	}
   158  }
   159  
   160  func (e *Engine) updatePartyAssetCache() {
   161  	e.cacheLock.Lock()
   162  	defer e.cacheLock.Unlock()
   163  	e.partyAssetCache = map[string]map[string]*num.Uint{}
   164  	e.partyMarketCache = map[string]map[string]*num.Uint{}
   165  	// initialise party market and general accounts
   166  	for k, v := range e.partiesAccs {
   167  		if k == "*" {
   168  			continue
   169  		}
   170  		general := map[string]*num.Uint{}
   171  		marketAccounts := map[string]*num.Uint{}
   172  		for _, a := range v {
   173  			if a.Type != types.AccountTypeGeneral && a.Type != types.AccountTypeHolding && a.Type != types.AccountTypeMargin && a.Type != types.AccountTypeOrderMargin {
   174  				continue
   175  			}
   176  			if a.Type == types.AccountTypeGeneral {
   177  				general[a.Asset] = a.Balance.Clone()
   178  			} else {
   179  				if _, ok := marketAccounts[a.MarketID]; !ok {
   180  					marketAccounts[a.MarketID] = num.UintZero()
   181  				}
   182  				marketAccounts[a.MarketID].AddSum(a.Balance)
   183  			}
   184  		}
   185  		e.partyAssetCache[k] = general
   186  		e.partyMarketCache[k] = marketAccounts
   187  	}
   188  }
   189  
   190  func (e *Engine) CheckOrderSpamAllMarkets(party string) error {
   191  	e.cacheLock.RLock()
   192  	defer e.cacheLock.RUnlock()
   193  	if _, ok := e.partyAssetCache[party]; !ok {
   194  		return fmt.Errorf("party " + party + " is not eligible to submit order transaction with no market in scope")
   195  	} else {
   196  		for _, asts := range e.partyAssetCache {
   197  			for _, balance := range asts {
   198  				if !balance.IsZero() {
   199  					return nil
   200  				}
   201  			}
   202  		}
   203  	}
   204  	if marketBalances, ok := e.partyMarketCache[party]; ok {
   205  		for _, marketBalance := range marketBalances {
   206  			if !marketBalance.IsZero() {
   207  				return nil
   208  			}
   209  		}
   210  	}
   211  	return fmt.Errorf("party " + party + " is not eligible to submit order transaction with no market in scope")
   212  }
   213  
   214  func (e *Engine) GetAllParties() []string {
   215  	keys := make([]string, 0, len(e.partiesAccsBalanceCache))
   216  	for k := range e.partiesAccsBalanceCache {
   217  		keys = append(keys, k)
   218  	}
   219  	sort.Strings(keys)
   220  	return keys
   221  }
   222  
   223  func (e *Engine) CheckOrderSpam(party, market string, assets []string) error {
   224  	e.cacheLock.RLock()
   225  	defer e.cacheLock.RUnlock()
   226  
   227  	if e.log.IsDebug() {
   228  		e.log.Debug("CheckOrderSpam", logging.String("party", party), logging.String("market", market), logging.Strings("assets", assets))
   229  	}
   230  	if assetBalances, ok := e.partyAssetCache[party]; !ok {
   231  		return fmt.Errorf("party " + party + " is not eligible to submit order transactions in market " + market + " (no general account in no asset)")
   232  	} else {
   233  		found := false
   234  		for _, ast := range assets {
   235  			if balance, ok := assetBalances[ast]; ok {
   236  				found = true
   237  				if !balance.IsZero() {
   238  					return nil
   239  				}
   240  			}
   241  		}
   242  		if !found {
   243  			if e.log.IsDebug() {
   244  				for ast, balance := range e.partyAssetCache[party] {
   245  					e.log.Debug("party asset cache", logging.String("party", party), logging.String("asset", ast), logging.String("balance", balance.String()))
   246  				}
   247  			}
   248  			return fmt.Errorf("party " + party + " is not eligible to submit order transactions in market " + market + " (no general account found)")
   249  		}
   250  	}
   251  
   252  	if marketBalances, ok := e.partyMarketCache[party]; ok {
   253  		if marketBalance, ok := marketBalances[market]; ok && !marketBalance.IsZero() {
   254  			return nil
   255  		} else {
   256  			return fmt.Errorf("party " + party + " is not eligible to submit order transactions in market " + market + " (no general account balance in asset and no market accounts found)")
   257  		}
   258  	}
   259  	return fmt.Errorf("party " + party + " is not eligible to submit order transactions in market " + market + " (no general account balance in asset and no balance in market accounts)")
   260  }
   261  
   262  func (e *Engine) BeginBlock(ctx context.Context) {
   263  	e.updatePartyAssetCache()
   264  	// FIXME(jeremy): to be removed after the migration from
   265  	// 72.x to 73, this will ensure all per assets accounts are being
   266  	// created after the restart
   267  	if !e.ensuredAssetAccounts {
   268  		// we don't want to do that again
   269  		e.ensuredAssetAccounts = true
   270  
   271  		assets := maps.Keys(e.enabledAssets)
   272  		sort.Strings(assets)
   273  		for _, assetId := range assets {
   274  			asset := e.enabledAssets[assetId]
   275  			e.ensureAllAssetAccounts(ctx, asset)
   276  		}
   277  	}
   278  	t := e.timeService.GetTimeNow()
   279  	if e.nextBalancesSnapshot.IsZero() || !e.nextBalancesSnapshot.After(t) {
   280  		e.updateNextBalanceSnapshot(t.Add(e.balanceSnapshotFrequency))
   281  		e.snapshotBalances()
   282  	}
   283  }
   284  
   285  func (e *Engine) snapshotBalances() {
   286  	e.partiesAccsBalanceCacheLock.Lock()
   287  	defer e.partiesAccsBalanceCacheLock.Unlock()
   288  	m := make(map[string]*num.Uint, len(e.partiesAccs))
   289  	quantums := map[string]*num.Uint{}
   290  	for k, v := range e.partiesAccs {
   291  		if k == "*" {
   292  			continue
   293  		}
   294  		total := num.UintZero()
   295  		for _, a := range v {
   296  			asset := a.Asset
   297  			if _, ok := quantums[asset]; !ok {
   298  				if _, ok := e.enabledAssets[asset]; !ok {
   299  					continue
   300  				}
   301  				quantum, _ := num.UintFromDecimal(e.enabledAssets[asset].Details.Quantum)
   302  				quantums[asset] = quantum
   303  			}
   304  			total.AddSum(num.UintZero().Div(a.Balance, quantums[asset]))
   305  		}
   306  		m[k] = total
   307  	}
   308  	e.partiesAccsBalanceCache = m
   309  }
   310  
   311  func (e *Engine) updateNextBalanceSnapshot(t time.Time) {
   312  	e.nextBalancesSnapshot = t
   313  	e.state.updateBalanceSnapshotTime(t)
   314  }
   315  
   316  func (e *Engine) OnBalanceSnapshotFrequencyUpdated(ctx context.Context, d time.Duration) error {
   317  	if !e.nextBalancesSnapshot.IsZero() {
   318  		e.updateNextBalanceSnapshot(e.nextBalancesSnapshot.Add(-e.balanceSnapshotFrequency))
   319  	}
   320  	e.balanceSnapshotFrequency = d
   321  	e.updateNextBalanceSnapshot(e.nextBalancesSnapshot.Add(d))
   322  	return nil
   323  }
   324  
   325  func (e *Engine) GetPartyBalance(party string) *num.Uint {
   326  	e.partiesAccsBalanceCacheLock.RLock()
   327  	defer e.partiesAccsBalanceCacheLock.RUnlock()
   328  
   329  	if balance, ok := e.partiesAccsBalanceCache[party]; ok {
   330  		return balance.Clone()
   331  	}
   332  	return num.UintZero()
   333  }
   334  
   335  func (e *Engine) GetAllVestingQuantumBalance(party string) num.Decimal {
   336  	balance := num.DecimalZero()
   337  
   338  	for asset, details := range e.enabledAssets {
   339  		// vesting balance
   340  		quantum := num.DecimalOne()
   341  		if !details.Details.Quantum.IsZero() {
   342  			quantum = details.Details.Quantum
   343  		}
   344  		if acc, ok := e.accs[e.accountID(noMarket, party, asset, types.AccountTypeVestingRewards)]; ok {
   345  			quantumBalance := acc.Balance.ToDecimal().Div(quantum)
   346  			balance = balance.Add(quantumBalance)
   347  		}
   348  
   349  		// vested balance
   350  		if acc, ok := e.accs[e.accountID(noMarket, party, asset, types.AccountTypeVestedRewards)]; ok {
   351  			quantumBalance := acc.Balance.ToDecimal().Div(quantum)
   352  			balance = balance.Add(quantumBalance)
   353  		}
   354  	}
   355  
   356  	return balance
   357  }
   358  
   359  func (e *Engine) GetAllVestingAndVestedAccountForAsset(asset string) []*types.Account {
   360  	accs := []*types.Account{}
   361  
   362  	for _, v := range e.hashableAccs {
   363  		if v.Asset == asset && (v.Type == types.AccountTypeVestingRewards || v.Type == types.AccountTypeVestedRewards) {
   364  			accs = append(accs, v.Clone())
   365  		}
   366  	}
   367  
   368  	return accs
   369  }
   370  
   371  func (e *Engine) GetVestingRecovery() map[string]map[string]*num.Uint {
   372  	out := e.vesting
   373  	e.vesting = map[string]map[string]*num.Uint{}
   374  	return out
   375  }
   376  
   377  func (e *Engine) addToVesting(
   378  	party, asset string,
   379  	balance *num.Uint,
   380  ) {
   381  	assets, ok := e.vesting[party]
   382  	if !ok {
   383  		assets = map[string]*num.Uint{}
   384  	}
   385  
   386  	assets[asset] = balance
   387  	e.vesting[party] = assets
   388  }
   389  
   390  func (e *Engine) addPartyAccount(party, accid string, acc *types.Account) {
   391  	accs, ok := e.partiesAccs[party]
   392  	if !ok {
   393  		accs = map[string]*types.Account{}
   394  		e.partiesAccs[party] = accs
   395  	}
   396  	// this is called only when an account is created first time
   397  	// and never twice
   398  	accs[accid] = acc
   399  }
   400  
   401  func (e *Engine) rmPartyAccount(party, accid string) {
   402  	// this cannot be called for a party which do not have an account already
   403  	// so no risk here
   404  	accs := e.partiesAccs[party]
   405  	delete(accs, accid)
   406  	// delete if the number of accounts for the party
   407  	// is down to 0
   408  	// FIXME(): for now we do not delete the
   409  	// party, this means that the numbner of
   410  	// party will grow forever if they were to
   411  	// get distressed, or people would be adding
   412  	// funds the withdrawing them forever on load
   413  	// of party, but that is better than having
   414  	// transaction stay in the mempool forever.
   415  	// if len(accs) <= 0 {
   416  	// 	delete(e.partiesAccs, party)
   417  	// }
   418  }
   419  
   420  func (e *Engine) removeAccountFromHashableSlice(id string) {
   421  	i := sort.Search(len(e.hashableAccs), func(i int) bool {
   422  		return e.hashableAccs[i].ID >= id
   423  	})
   424  
   425  	copy(e.hashableAccs[i:], e.hashableAccs[i+1:])
   426  	e.hashableAccs = e.hashableAccs[:len(e.hashableAccs)-1]
   427  	e.state.updateAccs(e.hashableAccs)
   428  }
   429  
   430  func (e *Engine) addAccountToHashableSlice(acc *types.Account) {
   431  	// sell side levels should be ordered in ascending
   432  	i := sort.Search(len(e.hashableAccs), func(i int) bool {
   433  		return e.hashableAccs[i].ID >= acc.ID
   434  	})
   435  
   436  	if i < len(e.hashableAccs) && e.hashableAccs[i].ID == acc.ID {
   437  		// for some reason it was already there, return now
   438  		return
   439  	}
   440  
   441  	e.hashableAccs = append(e.hashableAccs, nil)
   442  	copy(e.hashableAccs[i+1:], e.hashableAccs[i:])
   443  	e.hashableAccs[i] = acc
   444  	e.state.updateAccs(e.hashableAccs)
   445  }
   446  
   447  func (e *Engine) Hash() []byte {
   448  	output := make([]byte, len(e.hashableAccs)*32)
   449  	var i int
   450  	for _, k := range e.hashableAccs {
   451  		bal := k.Balance.Bytes()
   452  		copy(output[i:], bal[:])
   453  		i += 32
   454  	}
   455  
   456  	return crypto.Hash(output)
   457  }
   458  
   459  // ReloadConf updates the internal configuration of the collateral engine.
   460  func (e *Engine) ReloadConf(cfg Config) {
   461  	e.log.Info("reloading configuration")
   462  	if e.log.GetLevel() != cfg.Level.Get() {
   463  		e.log.Info("updating log level",
   464  			logging.String("old", e.log.GetLevel().String()),
   465  			logging.String("new", cfg.Level.String()),
   466  		)
   467  		e.log.SetLevel(cfg.Level.Get())
   468  	}
   469  
   470  	e.cfgMu.Lock()
   471  	e.Config = cfg
   472  	e.cfgMu.Unlock()
   473  }
   474  
   475  func (e *Engine) OnEpochEvent(ctx context.Context, epoch types.Epoch) {
   476  	if epoch.Action == vega.EpochAction_EPOCH_ACTION_START {
   477  		e.snapshotBalances()
   478  	}
   479  }
   480  
   481  func (e *Engine) OnEpochRestore(ctx context.Context, epoch types.Epoch) {}
   482  
   483  // EnableAsset adds a new asset in the collateral engine
   484  // this enable the asset to be used by new markets or
   485  // parties to deposit funds.
   486  func (e *Engine) EnableAsset(ctx context.Context, asset types.Asset) error {
   487  	if e.AssetExists(asset.ID) {
   488  		return ErrAssetAlreadyEnabled
   489  	}
   490  	e.enabledAssets[asset.ID] = asset
   491  	// update state
   492  	e.state.enableAsset(asset)
   493  
   494  	e.ensureAllAssetAccounts(ctx, asset)
   495  
   496  	e.log.Info("new asset added successfully",
   497  		logging.AssetID(asset.ID),
   498  	)
   499  	return nil
   500  }
   501  
   502  // ensureAllAssetAccounts will try to get all asset specific accounts
   503  // and if they do not exists will create them and send an event
   504  // this is useful when doing a migration so we can create all asset
   505  // account for already enabled assets.
   506  func (e *Engine) ensureAllAssetAccounts(ctx context.Context, asset types.Asset) {
   507  	e.log.Debug("ensureAllAssetAccounts started")
   508  	// then creat a new infrastructure fee account for the asset
   509  	// these are fee related account only
   510  	infraFeeID := e.accountID(noMarket, systemOwner, asset.ID, types.AccountTypeFeesInfrastructure)
   511  	_, ok := e.accs[infraFeeID]
   512  	if !ok {
   513  		infraFeeAcc := &types.Account{
   514  			ID:       infraFeeID,
   515  			Asset:    asset.ID,
   516  			Owner:    systemOwner,
   517  			Balance:  num.UintZero(),
   518  			MarketID: noMarket,
   519  			Type:     types.AccountTypeFeesInfrastructure,
   520  		}
   521  		e.accs[infraFeeID] = infraFeeAcc
   522  		e.addAccountToHashableSlice(infraFeeAcc)
   523  		e.broker.Send(events.NewAccountEvent(ctx, *infraFeeAcc))
   524  	}
   525  	externalID := e.accountID(noMarket, systemOwner, asset.ID, types.AccountTypeExternal)
   526  	if _, ok := e.accs[externalID]; !ok {
   527  		externalAcc := &types.Account{
   528  			ID:       externalID,
   529  			Asset:    asset.ID,
   530  			Owner:    systemOwner,
   531  			Balance:  num.UintZero(),
   532  			MarketID: noMarket,
   533  			Type:     types.AccountTypeExternal,
   534  		}
   535  		e.accs[externalID] = externalAcc
   536  
   537  		// This account originally wan't added to the app-state hash of accounts because it can always be "reconstructed" from
   538  		// the withdrawal/deposits in banking. For snapshotting we need to restore it and so instead of trying to make
   539  		// something thats already complicated more complex. we're just going to include it in the apphash which then gets
   540  		// included in the snapshot.
   541  		// see https://github.com/vegaprotocol/vega/pull/2745 for more information
   542  		e.addAccountToHashableSlice(externalAcc)
   543  		e.broker.Send(events.NewAccountEvent(ctx, *externalAcc))
   544  	}
   545  
   546  	// when an asset is enabled a staking reward account is created for it
   547  	rewardAccountTypes := []vega.AccountType{types.AccountTypeGlobalReward}
   548  	for _, rewardAccountType := range rewardAccountTypes {
   549  		rewardID := e.accountID(noMarket, systemOwner, asset.ID, rewardAccountType)
   550  		if _, ok := e.accs[rewardID]; !ok {
   551  			rewardAcc := &types.Account{
   552  				ID:       rewardID,
   553  				Asset:    asset.ID,
   554  				Owner:    systemOwner,
   555  				Balance:  num.UintZero(),
   556  				MarketID: noMarket,
   557  				Type:     rewardAccountType,
   558  			}
   559  			e.accs[rewardID] = rewardAcc
   560  			e.addAccountToHashableSlice(rewardAcc)
   561  			e.broker.Send(events.NewAccountEvent(ctx, *rewardAcc))
   562  		}
   563  	}
   564  
   565  	// network treasury for the asset
   566  	netTreasury := e.accountID(noMarket, systemOwner, asset.ID, types.AccountTypeNetworkTreasury)
   567  	if _, ok := e.accs[netTreasury]; !ok {
   568  		ntAcc := &types.Account{
   569  			ID:       netTreasury,
   570  			Asset:    asset.ID,
   571  			Owner:    systemOwner,
   572  			Balance:  num.UintZero(),
   573  			MarketID: noMarket,
   574  			Type:     types.AccountTypeNetworkTreasury,
   575  		}
   576  		e.accs[netTreasury] = ntAcc
   577  		e.addAccountToHashableSlice(ntAcc)
   578  		e.broker.Send(events.NewAccountEvent(ctx, *ntAcc))
   579  	}
   580  
   581  	// global insurance for the asset
   582  	globalInsurance := e.accountID(noMarket, systemOwner, asset.ID, types.AccountTypeGlobalInsurance)
   583  	if _, ok := e.accs[globalInsurance]; !ok {
   584  		giAcc := &types.Account{
   585  			ID:       globalInsurance,
   586  			Asset:    asset.ID,
   587  			Owner:    systemOwner,
   588  			Balance:  num.UintZero(),
   589  			MarketID: noMarket,
   590  			Type:     types.AccountTypeGlobalInsurance,
   591  		}
   592  		e.accs[globalInsurance] = giAcc
   593  		e.addAccountToHashableSlice(giAcc)
   594  		e.broker.Send(events.NewAccountEvent(ctx, *giAcc))
   595  	}
   596  
   597  	// pending transfers account
   598  	pendingTransfersID := e.accountID(noMarket, systemOwner, asset.ID, types.AccountTypePendingTransfers)
   599  	if _, ok := e.accs[pendingTransfersID]; !ok {
   600  		pendingTransfersAcc := &types.Account{
   601  			ID:       pendingTransfersID,
   602  			Asset:    asset.ID,
   603  			Owner:    systemOwner,
   604  			Balance:  num.UintZero(),
   605  			MarketID: noMarket,
   606  			Type:     types.AccountTypePendingTransfers,
   607  		}
   608  
   609  		e.accs[pendingTransfersID] = pendingTransfersAcc
   610  		e.addAccountToHashableSlice(pendingTransfersAcc)
   611  		e.broker.Send(events.NewAccountEvent(ctx, *pendingTransfersAcc))
   612  	}
   613  
   614  	pendingFeeReferrerRewardID := e.accountID(noMarket, systemOwner, asset.ID, types.AccountTypePendingFeeReferralReward)
   615  	if _, ok := e.accs[pendingFeeReferrerRewardID]; !ok {
   616  		pendingFeeReferrerRewardAcc := &types.Account{
   617  			ID:       pendingFeeReferrerRewardID,
   618  			Asset:    asset.ID,
   619  			Owner:    systemOwner,
   620  			Balance:  num.UintZero(),
   621  			MarketID: noMarket,
   622  			Type:     types.AccountTypePendingFeeReferralReward,
   623  		}
   624  
   625  		e.accs[pendingFeeReferrerRewardID] = pendingFeeReferrerRewardAcc
   626  		e.addAccountToHashableSlice(pendingFeeReferrerRewardAcc)
   627  		e.broker.Send(events.NewAccountEvent(ctx, *pendingFeeReferrerRewardAcc))
   628  	}
   629  }
   630  
   631  func (e *Engine) PropagateAssetUpdate(ctx context.Context, asset types.Asset) error {
   632  	if !e.AssetExists(asset.ID) {
   633  		return ErrAssetHasNotBeenEnabled
   634  	}
   635  	e.enabledAssets[asset.ID] = asset
   636  	e.state.updateAsset(asset)
   637  	// e.broker.Send(events.NewAssetEvent(ctx, asset))
   638  	return nil
   639  }
   640  
   641  // AssetExists no errors if the asset exists.
   642  func (e *Engine) AssetExists(assetID string) bool {
   643  	_, ok := e.enabledAssets[assetID]
   644  	return ok
   645  }
   646  
   647  func (e *Engine) GetInsurancePoolBalance(marketID, asset string) (*num.Uint, bool) {
   648  	insID := e.accountID(marketID, systemOwner, asset, types.AccountTypeInsurance)
   649  	if ins, err := e.GetAccountByID(insID); err == nil {
   650  		return ins.Balance.Clone(), true
   651  	}
   652  	return nil, false
   653  }
   654  
   655  func (e *Engine) SuccessorInsuranceFraction(ctx context.Context, successor, parent, asset string, fraction num.Decimal) *types.LedgerMovement {
   656  	pInsB, ok := e.GetInsurancePoolBalance(parent, asset)
   657  	if !ok || pInsB.IsZero() {
   658  		return nil
   659  	}
   660  	frac, _ := num.UintFromDecimal(num.DecimalFromUint(pInsB).Mul(fraction).Floor())
   661  	if frac.IsZero() {
   662  		return nil
   663  	}
   664  	insID := e.accountID(parent, systemOwner, asset, types.AccountTypeInsurance)
   665  	pIns, _ := e.GetAccountByID(insID)
   666  	sIns := e.GetOrCreateMarketInsurancePoolAccount(ctx, successor, asset)
   667  	// create transfer
   668  	req := &types.TransferRequest{
   669  		FromAccount: []*types.Account{
   670  			pIns,
   671  		},
   672  		ToAccount: []*types.Account{
   673  			sIns,
   674  		},
   675  		Amount:    frac,
   676  		MinAmount: frac.Clone(),
   677  		Asset:     asset,
   678  		Type:      types.TransferTypeSuccessorInsuranceFraction,
   679  	}
   680  	le, _ := e.getLedgerEntries(ctx, req)
   681  	if le == nil {
   682  		return nil
   683  	}
   684  	for _, bal := range le.Balances {
   685  		if err := e.IncrementBalance(ctx, bal.Account.ID, bal.Balance); err != nil {
   686  			e.log.Error("Could not update the target account in transfer",
   687  				logging.String("account-id", bal.Account.ID),
   688  				logging.Error(err))
   689  			return le
   690  		}
   691  	}
   692  	return le
   693  }
   694  
   695  // this func uses named returns because it makes body of the func look clearer.
   696  func (e *Engine) getSystemAccounts(marketID, asset string) (settle, insurance *types.Account, err error) {
   697  	insID := e.accountID(marketID, systemOwner, asset, types.AccountTypeInsurance)
   698  	setID := e.accountID(marketID, systemOwner, asset, types.AccountTypeSettlement)
   699  
   700  	if insurance, err = e.GetAccountByID(insID); err != nil {
   701  		if e.log.GetLevel() == logging.DebugLevel {
   702  			e.log.Debug("missing system account",
   703  				logging.String("asset", asset),
   704  				logging.String("id", insID),
   705  				logging.String("market", marketID),
   706  				logging.Error(err),
   707  			)
   708  		}
   709  		err = ErrSystemAccountsMissing
   710  		return
   711  	}
   712  
   713  	if settle, err = e.GetAccountByID(setID); err != nil {
   714  		if e.log.GetLevel() == logging.DebugLevel {
   715  			e.log.Debug("missing system account",
   716  				logging.String("asset", asset),
   717  				logging.String("id", setID),
   718  				logging.String("market", marketID),
   719  				logging.Error(err),
   720  			)
   721  		}
   722  		err = ErrSystemAccountsMissing
   723  	}
   724  
   725  	return
   726  }
   727  
   728  func (e *Engine) TransferSpotFeesContinuousTrading(ctx context.Context, marketID string, assetID string, ft events.FeesTransfer) ([]*types.LedgerMovement, error) {
   729  	if len(ft.Transfers()) <= 0 {
   730  		return nil, nil
   731  	}
   732  	// Check quickly that all parties have enough monies in their accounts.
   733  	// This may be done only in case of continuous trading.
   734  	for party, amount := range ft.TotalFeesAmountPerParty() {
   735  		generalAcc, err := e.GetAccountByID(e.accountID(noMarket, party, assetID, types.AccountTypeGeneral))
   736  		if err != nil {
   737  			e.log.Error("unable to get party account",
   738  				logging.String("account-type", "general"),
   739  				logging.String("party-id", party),
   740  				logging.String("asset", assetID))
   741  			return nil, ErrAccountDoesNotExist
   742  		}
   743  
   744  		if generalAcc.Balance.LT(amount) {
   745  			return nil, ErrInsufficientFundsToPayFees
   746  		}
   747  	}
   748  
   749  	return e.transferSpotFees(ctx, marketID, assetID, ft, types.AccountTypeGeneral)
   750  }
   751  
   752  func (e *Engine) TransferSpotFees(ctx context.Context, marketID string, assetID string, ft events.FeesTransfer, fromAccountType types.AccountType) ([]*types.LedgerMovement, error) {
   753  	return e.transferSpotFees(ctx, marketID, assetID, ft, fromAccountType)
   754  }
   755  
   756  func (e *Engine) transferSpotFees(ctx context.Context, marketID string, assetID string, ft events.FeesTransfer, fromAccountType types.AccountType) ([]*types.LedgerMovement, error) {
   757  	makerFee, infraFee, liquiFee, err := e.getFeesAccounts(marketID, assetID)
   758  	if err != nil {
   759  		return nil, err
   760  	}
   761  
   762  	transfers := ft.Transfers()
   763  	responses := make([]*types.LedgerMovement, 0, len(transfers))
   764  
   765  	for _, transfer := range transfers {
   766  		req, err := e.getSpotFeeTransferRequest(
   767  			ctx, transfer, makerFee, infraFee, liquiFee, marketID, assetID, fromAccountType)
   768  		if err != nil {
   769  			e.log.Error("Failed to build transfer request for event",
   770  				logging.Error(err))
   771  			return nil, err
   772  		}
   773  
   774  		if req == nil {
   775  			continue
   776  		}
   777  
   778  		res, err := e.getLedgerEntries(ctx, req)
   779  		if err != nil {
   780  			e.log.Error("Failed to transfer funds", logging.Error(err))
   781  			return nil, err
   782  		}
   783  		for _, bal := range res.Balances {
   784  			if err := e.IncrementBalance(ctx, bal.Account.ID, bal.Balance); err != nil {
   785  				e.log.Error("Could not update the target account in transfer",
   786  					logging.String("account-id", bal.Account.ID),
   787  					logging.Error(err))
   788  				return nil, err
   789  			}
   790  		}
   791  		responses = append(responses, res)
   792  	}
   793  
   794  	return responses, nil
   795  }
   796  
   797  func (e *Engine) getSpotFeeTransferRequest(
   798  	ctx context.Context,
   799  	t *types.Transfer,
   800  	makerFee, infraFee, liquiFee *types.Account,
   801  	marketID, assetID string,
   802  	sourceAccountType types.AccountType,
   803  ) (*types.TransferRequest, error) {
   804  	getAccount := func(marketID, owner string, accountType vega.AccountType) (*types.Account, error) {
   805  		acc, err := e.GetAccountByID(e.accountID(marketID, owner, assetID, accountType))
   806  		if err != nil {
   807  			e.log.Error(
   808  				fmt.Sprintf("Failed to get the %q %q account", owner, accountType),
   809  				logging.String("owner-id", t.Owner),
   810  				logging.String("market-id", marketID),
   811  				logging.Error(err),
   812  			)
   813  			return nil, err
   814  		}
   815  
   816  		return acc, nil
   817  	}
   818  
   819  	partyLiquidityFeeAccount := func() (*types.Account, error) {
   820  		return getAccount(marketID, t.Owner, types.AccountTypeLPLiquidityFees)
   821  	}
   822  
   823  	bonusDistributionAccount := func() (*types.Account, error) {
   824  		return getAccount(marketID, systemOwner, types.AccountTypeLiquidityFeesBonusDistribution)
   825  	}
   826  
   827  	accTypeForGeneral := types.AccountTypeGeneral
   828  	owner := t.Owner
   829  	if t.Owner == types.NetworkParty {
   830  		owner = systemOwner
   831  		accTypeForGeneral = sourceAccountType
   832  	}
   833  	general, err := getAccount(noMarket, owner, accTypeForGeneral)
   834  	if err != nil {
   835  		return nil, err
   836  	}
   837  
   838  	networkTreausry, err := e.GetNetworkTreasuryAccount(assetID)
   839  	if err != nil {
   840  		return nil, err
   841  	}
   842  
   843  	buyBackAccount := e.getOrCreateBuyBackFeesAccount(ctx, assetID)
   844  
   845  	treq := &types.TransferRequest{
   846  		Amount:    t.Amount.Amount.Clone(),
   847  		MinAmount: t.Amount.Amount.Clone(),
   848  		Asset:     assetID,
   849  		Type:      t.Type,
   850  	}
   851  
   852  	switch t.Type {
   853  	case types.TransferTypeInfrastructureFeePay:
   854  		amt := num.Min(treq.Amount, general.Balance.Clone())
   855  		treq.Amount = amt
   856  		treq.MinAmount = amt
   857  		treq.FromAccount = []*types.Account{general}
   858  		treq.ToAccount = []*types.Account{infraFee}
   859  		return treq, nil
   860  	case types.TransferTypeInfrastructureFeeDistribute:
   861  		treq.FromAccount = []*types.Account{infraFee}
   862  		treq.ToAccount = []*types.Account{general}
   863  		return treq, nil
   864  	case types.TransferTypeTreasuryPay:
   865  		amt := num.Min(treq.Amount, general.Balance.Clone())
   866  		treq.Amount = amt
   867  		treq.MinAmount = amt
   868  		treq.FromAccount = []*types.Account{general}
   869  		treq.ToAccount = []*types.Account{networkTreausry}
   870  		return treq, nil
   871  	case types.TransferTypeBuyBackFeePay:
   872  		amt := num.Min(treq.Amount, general.Balance.Clone())
   873  		treq.Amount = amt
   874  		treq.MinAmount = amt
   875  		treq.FromAccount = []*types.Account{general}
   876  		treq.ToAccount = []*types.Account{buyBackAccount}
   877  		return treq, nil
   878  	case types.TransferTypeLiquidityFeePay:
   879  		amt := num.Min(treq.Amount, general.Balance.Clone())
   880  		treq.Amount = amt
   881  		treq.MinAmount = amt
   882  		treq.FromAccount = []*types.Account{general}
   883  		treq.ToAccount = []*types.Account{liquiFee}
   884  		return treq, nil
   885  	case types.TransferTypeLiquidityFeeDistribute:
   886  		treq.FromAccount = []*types.Account{liquiFee}
   887  		treq.ToAccount = []*types.Account{general}
   888  		return treq, nil
   889  	case types.TransferTypeMakerFeePay:
   890  		amt := num.Min(treq.Amount, general.Balance.Clone())
   891  		treq.Amount = amt
   892  		treq.MinAmount = amt
   893  		treq.FromAccount = []*types.Account{general}
   894  		treq.ToAccount = []*types.Account{makerFee}
   895  		return treq, nil
   896  	case types.TransferTypeMakerFeeReceive:
   897  		treq.FromAccount = []*types.Account{makerFee}
   898  		treq.ToAccount = []*types.Account{general}
   899  		return treq, nil
   900  	case types.TransferTypeHighMakerRebateReceive:
   901  		treq.FromAccount = []*types.Account{makerFee}
   902  		treq.ToAccount = []*types.Account{general}
   903  		return treq, nil
   904  	case types.TransferTypeHighMakerRebatePay:
   905  		amt := num.Min(treq.Amount, general.Balance.Clone())
   906  		treq.Amount = amt
   907  		treq.MinAmount = amt
   908  		treq.FromAccount = []*types.Account{general}
   909  		treq.ToAccount = []*types.Account{makerFee}
   910  		return treq, nil
   911  	case types.TransferTypeLiquidityFeeAllocate:
   912  		partyLiquidityFee, err := partyLiquidityFeeAccount()
   913  		if err != nil {
   914  			return nil, err
   915  		}
   916  
   917  		treq.FromAccount = []*types.Account{liquiFee}
   918  		treq.ToAccount = []*types.Account{partyLiquidityFee}
   919  		return treq, nil
   920  	case types.TransferTypeLiquidityFeeNetDistribute:
   921  		partyLiquidityFee, err := partyLiquidityFeeAccount()
   922  		if err != nil {
   923  			return nil, err
   924  		}
   925  
   926  		treq.FromAccount = []*types.Account{partyLiquidityFee}
   927  		treq.ToAccount = []*types.Account{general}
   928  		return treq, nil
   929  	case types.TransferTypeLiquidityFeeUnpaidCollect:
   930  		partyLiquidityFee, err := partyLiquidityFeeAccount()
   931  		if err != nil {
   932  			return nil, err
   933  		}
   934  		bonusDistribution, err := bonusDistributionAccount()
   935  		if err != nil {
   936  			return nil, err
   937  		}
   938  
   939  		treq.FromAccount = []*types.Account{partyLiquidityFee}
   940  		treq.ToAccount = []*types.Account{bonusDistribution}
   941  		return treq, nil
   942  	case types.TransferTypeSlaPerformanceBonusDistribute:
   943  		bonusDistribution, err := bonusDistributionAccount()
   944  		if err != nil {
   945  			return nil, err
   946  		}
   947  
   948  		treq.FromAccount = []*types.Account{bonusDistribution}
   949  		treq.ToAccount = []*types.Account{general}
   950  		return treq, nil
   951  	case types.TransferTypeSLAPenaltyLpFeeApply:
   952  		partyLiquidityFee, err := partyLiquidityFeeAccount()
   953  		if err != nil {
   954  			return nil, err
   955  		}
   956  		networkTreasury, err := e.GetNetworkTreasuryAccount(assetID)
   957  		if err != nil {
   958  			return nil, err
   959  		}
   960  
   961  		treq.FromAccount = []*types.Account{partyLiquidityFee}
   962  		treq.ToAccount = []*types.Account{networkTreasury}
   963  		return treq, nil
   964  	default:
   965  		return nil, ErrInvalidTransferTypeForFeeRequest
   966  	}
   967  }
   968  
   969  func (e *Engine) TransferFees(ctx context.Context, marketID string, assetID string, ft events.FeesTransfer) ([]*types.LedgerMovement, error) {
   970  	return e.transferFees(ctx, marketID, assetID, ft)
   971  }
   972  
   973  // returns the corresponding transfer request for the slice of transfers
   974  // if the reward accound doesn't exist return error
   975  // if the party account doesn't exist log the error and continue.
   976  func (e *Engine) getRewardTransferRequests(ctx context.Context, rewardAccountID string, transfers []*types.Transfer, rewardType types.AccountType) ([]*types.TransferRequest, error) {
   977  	rewardAccount, err := e.GetAccountByID(rewardAccountID)
   978  	if err != nil {
   979  		return nil, err
   980  	}
   981  
   982  	rewardTRs := make([]*types.TransferRequest, 0, len(transfers))
   983  	for _, t := range transfers {
   984  		var destination *types.Account
   985  		if rewardType == types.AccountTypeFeesInfrastructure {
   986  			destination, err = e.GetPartyGeneralAccount(t.Owner, t.Amount.Asset)
   987  			if err != nil {
   988  				e.CreatePartyGeneralAccount(ctx, t.Owner, t.Amount.Asset)
   989  				destination, _ = e.GetPartyGeneralAccount(t.Owner, t.Amount.Asset)
   990  			}
   991  		} else {
   992  			destination = e.GetOrCreatePartyVestingRewardAccount(ctx, t.Owner, t.Amount.Asset)
   993  		}
   994  
   995  		rewardTRs = append(rewardTRs, &types.TransferRequest{
   996  			Amount:      t.Amount.Amount.Clone(),
   997  			MinAmount:   t.Amount.Amount.Clone(),
   998  			Asset:       t.Amount.Asset,
   999  			Type:        types.TransferTypeRewardPayout,
  1000  			FromAccount: []*types.Account{rewardAccount},
  1001  			ToAccount:   []*types.Account{destination},
  1002  		})
  1003  	}
  1004  	return rewardTRs, nil
  1005  }
  1006  
  1007  // TransferRewards takes a slice of transfers and serves them to transfer rewards from the reward account to parties general account.
  1008  func (e *Engine) TransferRewards(ctx context.Context, rewardAccountID string, transfers []*types.Transfer, rewardType types.AccountType) ([]*types.LedgerMovement, error) {
  1009  	responses := make([]*types.LedgerMovement, 0, len(transfers))
  1010  
  1011  	if len(transfers) == 0 {
  1012  		return responses, nil
  1013  	}
  1014  	transferReqs, err := e.getRewardTransferRequests(ctx, rewardAccountID, transfers, rewardType)
  1015  	if err != nil {
  1016  		return nil, err
  1017  	}
  1018  
  1019  	for _, req := range transferReqs {
  1020  		res, err := e.getLedgerEntries(ctx, req)
  1021  		if err != nil {
  1022  			e.log.Error("Failed to transfer funds", logging.Error(err))
  1023  			return nil, err
  1024  		}
  1025  		for _, bal := range res.Balances {
  1026  			if err := e.IncrementBalance(ctx, bal.Account.ID, bal.Balance); err != nil {
  1027  				e.log.Error("Could not update the target account in transfer",
  1028  					logging.String("account-id", bal.Account.ID),
  1029  					logging.Error(err))
  1030  				return nil, err
  1031  			}
  1032  		}
  1033  		responses = append(responses, res)
  1034  	}
  1035  
  1036  	return responses, nil
  1037  }
  1038  
  1039  func (e *Engine) TransferVestedRewards(ctx context.Context, transfers []*types.Transfer) ([]*types.LedgerMovement, error) {
  1040  	if len(transfers) == 0 {
  1041  		return nil, nil
  1042  	}
  1043  
  1044  	transferReqs := make([]*types.TransferRequest, 0, len(transfers))
  1045  	for _, t := range transfers {
  1046  		transferReqs = append(transferReqs, &types.TransferRequest{
  1047  			FromAccount: []*types.Account{
  1048  				e.GetOrCreatePartyVestingRewardAccount(ctx, t.Owner, t.Amount.Asset),
  1049  			},
  1050  			ToAccount: []*types.Account{
  1051  				e.GetOrCreatePartyVestedRewardAccount(ctx, t.Owner, t.Amount.Asset),
  1052  			},
  1053  			Amount:    t.Amount.Amount.Clone(),
  1054  			MinAmount: t.MinAmount.Clone(),
  1055  			Asset:     t.Amount.Asset,
  1056  			Type:      t.Type,
  1057  		})
  1058  	}
  1059  
  1060  	responses := make([]*types.LedgerMovement, 0, len(transfers))
  1061  	for _, req := range transferReqs {
  1062  		res, err := e.getLedgerEntries(ctx, req)
  1063  		if err != nil {
  1064  			e.log.Error("Failed to transfer funds", logging.Error(err))
  1065  			return nil, err
  1066  		}
  1067  		for _, bal := range res.Balances {
  1068  			if err := e.IncrementBalance(ctx, bal.Account.ID, bal.Balance); err != nil {
  1069  				e.log.Error("Could not update the target account in transfer",
  1070  					logging.String("account-id", bal.Account.ID),
  1071  					logging.Error(err))
  1072  				return nil, err
  1073  			}
  1074  		}
  1075  		responses = append(responses, res)
  1076  	}
  1077  
  1078  	return responses, nil
  1079  }
  1080  
  1081  func (e *Engine) TransferFeesContinuousTrading(ctx context.Context, marketID string, assetID string, ft events.FeesTransfer) ([]*types.LedgerMovement, error) {
  1082  	if len(ft.Transfers()) <= 0 {
  1083  		return nil, nil
  1084  	}
  1085  	// check quickly that all parties have enough monies in their accounts
  1086  	// this may be done only in case of continuous trading
  1087  	for party, amount := range ft.TotalFeesAmountPerParty() {
  1088  		generalAcc, err := e.GetAccountByID(e.accountID(noMarket, party, assetID, types.AccountTypeGeneral))
  1089  		if err != nil {
  1090  			e.log.Error("unable to get party account",
  1091  				logging.String("account-type", "general"),
  1092  				logging.String("party-id", party),
  1093  				logging.String("asset", assetID))
  1094  			return nil, ErrAccountDoesNotExist
  1095  		}
  1096  
  1097  		marginAcc, err := e.GetAccountByID(e.accountID(marketID, party, assetID, types.AccountTypeMargin))
  1098  		if err != nil {
  1099  			e.log.Error("unable to get party account",
  1100  				logging.String("account-type", "margin"),
  1101  				logging.String("party-id", party),
  1102  				logging.String("asset", assetID),
  1103  				logging.String("market-id", marketID))
  1104  			return nil, ErrAccountDoesNotExist
  1105  		}
  1106  
  1107  		if num.Sum(marginAcc.Balance, generalAcc.Balance).LT(amount) {
  1108  			return nil, ErrInsufficientFundsToPayFees
  1109  		}
  1110  	}
  1111  
  1112  	return e.transferFees(ctx, marketID, assetID, ft)
  1113  }
  1114  
  1115  func (e *Engine) PartyCanCoverFees(asset, mktID, partyID string, amount *num.Uint) error {
  1116  	generalAccount, err := e.GetPartyGeneralAccount(partyID, asset)
  1117  	if err != nil {
  1118  		return err
  1119  	}
  1120  	generalAccountBalance := generalAccount.Balance
  1121  	marginAccount, _ := e.GetPartyMarginAccount(mktID, partyID, asset)
  1122  
  1123  	marginAccountBalance := num.UintZero()
  1124  	if marginAccount != nil {
  1125  		marginAccountBalance = marginAccount.Balance
  1126  	}
  1127  
  1128  	if num.Sum(generalAccountBalance, marginAccountBalance).LT(amount) {
  1129  		return fmt.Errorf("party has insufficient funds to cover fees")
  1130  	}
  1131  	return nil
  1132  }
  1133  
  1134  func (e *Engine) transferFees(ctx context.Context, marketID string, assetID string, ft events.FeesTransfer) ([]*types.LedgerMovement, error) {
  1135  	makerFee, infraFee, liquiFee, err := e.getFeesAccounts(marketID, assetID)
  1136  	if err != nil {
  1137  		return nil, err
  1138  	}
  1139  
  1140  	transfers := ft.Transfers()
  1141  	responses := make([]*types.LedgerMovement, 0, len(transfers))
  1142  
  1143  	for _, transfer := range transfers {
  1144  		req, err := e.getFeeTransferRequest(ctx,
  1145  			transfer, makerFee, infraFee, liquiFee, marketID, assetID)
  1146  		if err != nil {
  1147  			e.log.Error("Failed to build transfer request for event",
  1148  				logging.Error(err))
  1149  			return nil, err
  1150  		}
  1151  
  1152  		res, err := e.getLedgerEntries(ctx, req)
  1153  		if err != nil {
  1154  			e.log.Error("Failed to transfer funds", logging.Error(err))
  1155  			return nil, err
  1156  		}
  1157  		for _, bal := range res.Balances {
  1158  			if err := e.IncrementBalance(ctx, bal.Account.ID, bal.Balance); err != nil {
  1159  				e.log.Error("Could not update the target account in transfer",
  1160  					logging.String("account-id", bal.Account.ID),
  1161  					logging.Error(err))
  1162  				return nil, err
  1163  			}
  1164  		}
  1165  		responses = append(responses, res)
  1166  	}
  1167  
  1168  	return responses, nil
  1169  }
  1170  
  1171  // GetInfraFeeAccountIDs returns the account IDs of the infrastructure fee accounts for all enabled assets.
  1172  func (e *Engine) GetInfraFeeAccountIDs() []string {
  1173  	accountIDs := []string{}
  1174  	for asset := range e.enabledAssets {
  1175  		accountIDs = append(accountIDs, e.accountID(noMarket, systemOwner, asset, types.AccountTypeFeesInfrastructure))
  1176  	}
  1177  	sort.Strings(accountIDs)
  1178  	return accountIDs
  1179  }
  1180  
  1181  // GetPendingTransferAccount return the pending transfers account for the asset.
  1182  func (e *Engine) GetPendingTransfersAccount(asset string) *types.Account {
  1183  	acc, err := e.GetAccountByID(e.accountID(noMarket, systemOwner, asset, types.AccountTypePendingTransfers))
  1184  	if err != nil {
  1185  		e.log.Panic("no pending transfers account for asset, this should never happen",
  1186  			logging.AssetID(asset),
  1187  		)
  1188  	}
  1189  
  1190  	return acc
  1191  }
  1192  
  1193  // this func uses named returns because it makes body of the func look clearer.
  1194  func (e *Engine) getFeesAccounts(marketID, asset string) (maker, infra, liqui *types.Account, err error) {
  1195  	makerID := e.accountID(marketID, systemOwner, asset, types.AccountTypeFeesMaker)
  1196  	infraID := e.accountID(noMarket, systemOwner, asset, types.AccountTypeFeesInfrastructure)
  1197  	liquiID := e.accountID(marketID, systemOwner, asset, types.AccountTypeFeesLiquidity)
  1198  
  1199  	if maker, err = e.GetAccountByID(makerID); err != nil {
  1200  		if e.log.GetLevel() == logging.DebugLevel {
  1201  			e.log.Debug("missing fee account",
  1202  				logging.String("asset", asset),
  1203  				logging.String("id", makerID),
  1204  				logging.String("market", marketID),
  1205  				logging.Error(err),
  1206  			)
  1207  		}
  1208  		err = ErrFeeAccountsMissing
  1209  		return
  1210  	}
  1211  
  1212  	if infra, err = e.GetAccountByID(infraID); err != nil {
  1213  		if e.log.GetLevel() == logging.DebugLevel {
  1214  			e.log.Debug("missing fee account",
  1215  				logging.String("asset", asset),
  1216  				logging.String("id", infraID),
  1217  				logging.Error(err),
  1218  			)
  1219  		}
  1220  	}
  1221  
  1222  	if liqui, err = e.GetAccountByID(liquiID); err != nil {
  1223  		if e.log.GetLevel() == logging.DebugLevel {
  1224  			e.log.Debug("missing system account",
  1225  				logging.String("asset", asset),
  1226  				logging.String("id", liquiID),
  1227  				logging.String("market", marketID),
  1228  				logging.Error(err),
  1229  			)
  1230  		}
  1231  	}
  1232  
  1233  	if err != nil {
  1234  		err = ErrFeeAccountsMissing
  1235  	}
  1236  
  1237  	return maker, infra, liqui, err
  1238  }
  1239  
  1240  func (e *Engine) CheckLeftOverBalance(ctx context.Context, settle *types.Account, transfers []*types.Transfer, asset string, factor *num.Uint) (*types.LedgerMovement, error) {
  1241  	if settle.Balance.IsZero() {
  1242  		return nil, nil
  1243  	}
  1244  	if factor == nil {
  1245  		factor = num.UintOne()
  1246  	}
  1247  
  1248  	e.log.Error("final settlement left asset unit in the settlement, transferring to the asset global insurance", logging.String("remaining-settle-balance", settle.Balance.String()))
  1249  	for _, t := range transfers {
  1250  		e.log.Error("final settlement transfer", logging.String("amount", t.Amount.String()), logging.Int32("type", int32(t.Type)))
  1251  	}
  1252  	// if there's just one asset unit left over from some weird rounding issue, transfer it to the global insurance
  1253  	if settle.Balance.LTE(factor) {
  1254  		e.log.Warn("final settlement left 1 asset unit in the settlement, transferring to the asset global insurance account")
  1255  		req := &types.TransferRequest{
  1256  			FromAccount: make([]*types.Account, 1),
  1257  			ToAccount:   make([]*types.Account, 1),
  1258  			Asset:       asset,
  1259  			Type:        types.TransferTypeClearAccount,
  1260  		}
  1261  		globalIns, _ := e.GetGlobalInsuranceAccount(asset)
  1262  		req.FromAccount[0] = settle
  1263  		req.ToAccount = []*types.Account{globalIns}
  1264  		req.Amount = settle.Balance.Clone()
  1265  		ledgerEntries, err := e.getLedgerEntries(ctx, req)
  1266  		if err != nil {
  1267  			e.log.Panic("unable to redistribute settlement leftover funds", logging.Error(err))
  1268  		}
  1269  		for _, bal := range ledgerEntries.Balances {
  1270  			if err := e.IncrementBalance(ctx, bal.Account.ID, bal.Balance); err != nil {
  1271  				e.log.Error("Could not update the target account in transfer",
  1272  					logging.String("account-id", bal.Account.ID),
  1273  					logging.Error(err))
  1274  				return nil, err
  1275  			}
  1276  		}
  1277  		return ledgerEntries, nil
  1278  	}
  1279  
  1280  	// if there's more than one, panic
  1281  	e.log.Panic("settlement balance is not zero", logging.BigUint("balance", settle.Balance))
  1282  	return nil, nil
  1283  }
  1284  
  1285  // FinalSettlement will process the list of transfers instructed by other engines
  1286  // This func currently only expects TransferType_{LOSS,WIN} transfers
  1287  // other transfer types have dedicated funcs (MarkToMarket, MarginUpdate).
  1288  func (e *Engine) FinalSettlement(ctx context.Context, marketID string, transfers []*types.Transfer, factor *num.Uint, useGeneralAccountForMarginSearch func(string) bool) ([]*types.LedgerMovement, error) {
  1289  	// stop immediately if there aren't any transfers, channels are closed
  1290  	if len(transfers) == 0 {
  1291  		return nil, nil
  1292  	}
  1293  	responses := make([]*types.LedgerMovement, 0, len(transfers))
  1294  	asset := transfers[0].Amount.Asset
  1295  
  1296  	var (
  1297  		lastWinID            int
  1298  		expectCollected      num.Decimal
  1299  		expCollected         = num.UintZero()
  1300  		totalAmountCollected = num.UintZero()
  1301  	)
  1302  
  1303  	now := e.timeService.GetTimeNow().UnixNano()
  1304  	brokerEvts := make([]events.Event, 0, len(transfers))
  1305  
  1306  	settle, insurance, err := e.getSystemAccounts(marketID, asset)
  1307  	if err != nil {
  1308  		e.log.Error(
  1309  			"Failed to get system accounts required for final settlement",
  1310  			logging.Error(err),
  1311  		)
  1312  		return nil, err
  1313  	}
  1314  
  1315  	// process loses first
  1316  	for i, transfer := range transfers {
  1317  		if transfer == nil {
  1318  			continue
  1319  		}
  1320  		if transfer.Type == types.TransferTypeWin {
  1321  			// we processed all losses break then
  1322  			lastWinID = i
  1323  			break
  1324  		}
  1325  
  1326  		req, err := e.getTransferRequest(transfer, settle, insurance, &marginUpdate{}, useGeneralAccountForMarginSearch(transfer.Owner))
  1327  		if err != nil {
  1328  			e.log.Error(
  1329  				"Failed to build transfer request for event",
  1330  				logging.Error(err),
  1331  			)
  1332  			return nil, err
  1333  		}
  1334  
  1335  		// accumulate the expected transfer size
  1336  		expCollected.AddSum(req.Amount)
  1337  		expectCollected = expectCollected.Add(num.DecimalFromUint(req.Amount))
  1338  		// doing a copy of the amount here, as the request is send to listLedgerEntries, which actually
  1339  		// modifies it
  1340  		requestAmount := req.Amount.Clone()
  1341  
  1342  		// set the amount (this can change the req.Amount value if we entered loss socialisation
  1343  		res, err := e.getLedgerEntries(ctx, req)
  1344  		if err != nil {
  1345  			e.log.Error(
  1346  				"Failed to transfer funds",
  1347  				logging.Error(err),
  1348  			)
  1349  			return nil, err
  1350  		}
  1351  		amountCollected := num.UintZero()
  1352  
  1353  		for _, bal := range res.Balances {
  1354  			amountCollected.AddSum(bal.Balance)
  1355  			if err := e.IncrementBalance(ctx, bal.Account.ID, bal.Balance); err != nil {
  1356  				e.log.Error(
  1357  					"Could not update the target account in transfer",
  1358  					logging.String("account-id", bal.Account.ID),
  1359  					logging.Error(err),
  1360  				)
  1361  				return nil, err
  1362  			}
  1363  		}
  1364  		totalAmountCollected.AddSum(amountCollected)
  1365  		responses = append(responses, res)
  1366  
  1367  		// Update to see how much we still need
  1368  		requestAmount.Sub(requestAmount, amountCollected)
  1369  		if transfer.Owner != types.NetworkParty {
  1370  			// no error possible here, we're just reloading the accounts to ensure the correct balance
  1371  			general, margin, bond, _ := e.getMTMPartyAccounts(transfer.Owner, marketID, asset)
  1372  
  1373  			totalInAccount := num.Sum(general.Balance, margin.Balance)
  1374  			if bond != nil {
  1375  				totalInAccount.Add(totalInAccount, bond.Balance)
  1376  			}
  1377  
  1378  			if totalInAccount.LT(requestAmount) {
  1379  				delta := req.Amount.Sub(requestAmount, totalInAccount)
  1380  				e.log.Warn("loss socialization missing amount to be collected or used from insurance pool",
  1381  					logging.String("party-id", transfer.Owner),
  1382  					logging.BigUint("amount", delta),
  1383  					logging.String("market-id", settle.MarketID))
  1384  
  1385  				brokerEvts = append(brokerEvts,
  1386  					events.NewLossSocializationEvent(ctx, transfer.Owner, marketID, delta, false, now, types.LossTypeUnspecified))
  1387  			}
  1388  		}
  1389  	}
  1390  
  1391  	if len(brokerEvts) > 0 {
  1392  		e.broker.SendBatch(brokerEvts)
  1393  	}
  1394  
  1395  	// if winidx is 0, this means we had now win and loss, but may have some event which
  1396  	// needs to be propagated forward so we return now.
  1397  	if lastWinID == 0 {
  1398  		if !settle.Balance.IsZero() {
  1399  			e.log.Panic("settlement balance is not zero", logging.BigUint("balance", settle.Balance))
  1400  		}
  1401  		return responses, nil
  1402  	}
  1403  
  1404  	// now compare what's in the settlement account what we expect initially to redistribute.
  1405  	// if there's not enough we enter loss socialization
  1406  	distr := simpleDistributor{
  1407  		log:             e.log,
  1408  		marketID:        marketID,
  1409  		expectCollected: expCollected,
  1410  		collected:       totalAmountCollected,
  1411  		requests:        make([]request, 0, len(transfers)-lastWinID),
  1412  		ts:              now,
  1413  	}
  1414  
  1415  	if distr.LossSocializationEnabled() {
  1416  		e.log.Warn("Entering loss socialization on final settlement",
  1417  			logging.String("market-id", marketID),
  1418  			logging.String("asset", asset),
  1419  			logging.BigUint("expect-collected", expCollected),
  1420  			logging.BigUint("collected", settle.Balance))
  1421  		for _, transfer := range transfers[lastWinID:] {
  1422  			if transfer != nil && transfer.Type == types.TransferTypeWin {
  1423  				distr.Add(transfer)
  1424  			}
  1425  		}
  1426  		if evts := distr.Run(ctx); len(evts) != 0 {
  1427  			e.broker.SendBatch(evts)
  1428  		}
  1429  	}
  1430  
  1431  	// then we process all the wins
  1432  	for _, transfer := range transfers[lastWinID:] {
  1433  		if transfer == nil {
  1434  			continue
  1435  		}
  1436  
  1437  		req, err := e.getTransferRequest(transfer, settle, insurance, &marginUpdate{}, useGeneralAccountForMarginSearch(transfer.Owner))
  1438  		if err != nil {
  1439  			e.log.Error(
  1440  				"Failed to build transfer request for event",
  1441  				logging.Error(err),
  1442  			)
  1443  			return nil, err
  1444  		}
  1445  
  1446  		// set the amount (this can change the req.Amount value if we entered loss socialisation
  1447  		res, err := e.getLedgerEntries(ctx, req)
  1448  		if err != nil {
  1449  			e.log.Error(
  1450  				"Failed to transfer funds",
  1451  				logging.Error(err),
  1452  			)
  1453  			return nil, err
  1454  		}
  1455  
  1456  		// update the to accounts now
  1457  		for _, bal := range res.Balances {
  1458  			if err := e.IncrementBalance(ctx, bal.Account.ID, bal.Balance); err != nil {
  1459  				e.log.Error(
  1460  					"Could not update the target account in transfer",
  1461  					logging.String("account-id", bal.Account.ID),
  1462  					logging.Error(err),
  1463  				)
  1464  				return nil, err
  1465  			}
  1466  		}
  1467  		responses = append(responses, res)
  1468  	}
  1469  
  1470  	leftoverLedgerEntry, err := e.CheckLeftOverBalance(ctx, settle, transfers, asset, factor)
  1471  	if err != nil {
  1472  		return nil, err
  1473  	}
  1474  	if leftoverLedgerEntry != nil {
  1475  		responses = append(responses, leftoverLedgerEntry)
  1476  	}
  1477  
  1478  	return responses, nil
  1479  }
  1480  
  1481  func (e *Engine) getMTMPartyAccounts(party, marketID, asset string) (gen, margin, bond *types.Account, err error) {
  1482  	// no need to look any further
  1483  	if party == types.NetworkParty {
  1484  		return nil, nil, nil, nil
  1485  	}
  1486  	gen, err = e.GetAccountByID(e.accountID(noMarket, party, asset, types.AccountTypeGeneral))
  1487  	if err != nil {
  1488  		return nil, nil, nil, err
  1489  	}
  1490  	margin, err = e.GetAccountByID(e.accountID(marketID, party, asset, types.AccountTypeMargin))
  1491  
  1492  	// do not check error, not all parties have a bond account
  1493  	bond, _ = e.GetAccountByID(e.accountID(marketID, party, asset, types.AccountTypeBond))
  1494  
  1495  	return
  1496  }
  1497  
  1498  // PerpsFundingSettlement will run a funding settlement over given positions.
  1499  // This works exactly the same as a MTM settlement, but uses different transfer types.
  1500  func (e *Engine) PerpsFundingSettlement(ctx context.Context, marketID string, transfers []events.Transfer, asset string, round *num.Uint, useGeneralAccountForMarginSearch func(string) bool) ([]events.Margin, []*types.LedgerMovement, error) {
  1501  	return e.mtmOrFundingSettlement(ctx, marketID, transfers, asset, types.TransferTypePerpFundingWin, round, useGeneralAccountForMarginSearch, types.LossTypeFunding)
  1502  }
  1503  
  1504  // MarkToMarket will run the mark to market settlement over a given set of positions
  1505  // return ledger move stuff here, too (separate return value, because we need to stream those).
  1506  func (e *Engine) MarkToMarket(ctx context.Context, marketID string, transfers []events.Transfer, asset string, useGeneralAccountForMarginSearch func(string) bool) ([]events.Margin, []*types.LedgerMovement, error) {
  1507  	return e.mtmOrFundingSettlement(ctx, marketID, transfers, asset, types.TransferTypeMTMWin, nil, useGeneralAccountForMarginSearch, types.LossTypeSettlement)
  1508  }
  1509  
  1510  func (e *Engine) mtmOrFundingSettlement(ctx context.Context, marketID string, transfers []events.Transfer, asset string, winType types.TransferType, round *num.Uint, useGeneralAccountForMarginSearch func(string) bool, lType types.LossType) ([]events.Margin, []*types.LedgerMovement, error) {
  1511  	// stop immediately if there aren't any transfers, channels are closed
  1512  	if len(transfers) == 0 {
  1513  		return nil, nil, nil
  1514  	}
  1515  	marginEvts := make([]events.Margin, 0, len(transfers))
  1516  	responses := make([]*types.LedgerMovement, 0, len(transfers))
  1517  
  1518  	// This is where we'll implement everything
  1519  	settle, insurance, err := e.getSystemAccounts(marketID, asset)
  1520  	if err != nil {
  1521  		e.log.Error(
  1522  			"Failed to get system accounts required for MTM settlement",
  1523  			logging.Error(err),
  1524  		)
  1525  		return nil, nil, err
  1526  	}
  1527  	// get the component that calculates the loss socialisation etc... if needed
  1528  
  1529  	var (
  1530  		winidx          int
  1531  		expectCollected num.Decimal
  1532  		expCollected    = num.UintZero()
  1533  	)
  1534  
  1535  	// create batch of events
  1536  	brokerEvts := make([]events.Event, 0, len(transfers))
  1537  	now := e.timeService.GetTimeNow().UnixNano()
  1538  
  1539  	// iterate over transfer until we get the first win, so we need we accumulated all loss
  1540  	for i, evt := range transfers {
  1541  		party := evt.Party()
  1542  		transfer := evt.Transfer()
  1543  		marginEvt := &marginUpdate{
  1544  			MarketPosition: evt,
  1545  			asset:          asset,
  1546  			marketID:       settle.MarketID,
  1547  		}
  1548  
  1549  		if party != types.NetworkParty {
  1550  			// get the state of the accounts before processing transfers
  1551  			// so they can be used in the marginEvt, and to calculate the missing funds
  1552  			marginEvt.general, marginEvt.margin, marginEvt.bond, err = e.getMTMPartyAccounts(party, settle.MarketID, asset)
  1553  			marginEvt.orderMargin, _ = e.GetAccountByID(e.accountID(marginEvt.marketID, party, marginEvt.asset, types.AccountTypeOrderMargin))
  1554  			if err != nil {
  1555  				e.log.Error("unable to get party account",
  1556  					logging.String("party-id", party),
  1557  					logging.String("asset", asset),
  1558  					logging.String("market-id", settle.MarketID))
  1559  			}
  1560  		}
  1561  
  1562  		// no transfer needed if transfer is nil, just build the marginUpdate
  1563  		if transfer == nil {
  1564  			// no error when getting MTM accounts, and no margin account == network position
  1565  			// we are not interested in this event, continue here
  1566  			marginEvts = append(marginEvts, marginEvt)
  1567  			continue
  1568  		}
  1569  
  1570  		if transfer.Type == winType {
  1571  			// we processed all loss break then
  1572  			winidx = i
  1573  			break
  1574  		}
  1575  
  1576  		req, err := e.getTransferRequest(transfer, settle, insurance, marginEvt, useGeneralAccountForMarginSearch(transfer.Owner))
  1577  		if err != nil {
  1578  			e.log.Error(
  1579  				"Failed to build transfer request for event",
  1580  				logging.Error(err),
  1581  			)
  1582  			return nil, nil, err
  1583  		}
  1584  		// accumulate the expected transfer size
  1585  		expCollected.AddSum(req.Amount)
  1586  		expectCollected = expectCollected.Add(num.DecimalFromUint(req.Amount))
  1587  		// doing a copy of the amount here, as the request is send to listLedgerEntries, which actually
  1588  		// modifies it
  1589  		requestAmount := req.Amount.Clone()
  1590  
  1591  		// set the amount (this can change the req.Amount value if we entered loss socialisation
  1592  		res, err := e.getLedgerEntries(ctx, req)
  1593  		if err != nil {
  1594  			e.log.Error(
  1595  				"Failed to transfer funds",
  1596  				logging.Error(err),
  1597  			)
  1598  			return nil, nil, err
  1599  		}
  1600  
  1601  		amountCollected := num.UintZero()
  1602  		// // update the to accounts now
  1603  		for _, bal := range res.Balances {
  1604  			amountCollected.AddSum(bal.Balance)
  1605  			if err := e.IncrementBalance(ctx, bal.Account.ID, bal.Balance); err != nil {
  1606  				e.log.Error(
  1607  					"Could not update the target account in transfer",
  1608  					logging.String("account-id", bal.Account.ID),
  1609  					logging.Error(err),
  1610  				)
  1611  				return nil, nil, err
  1612  			}
  1613  		}
  1614  		responses = append(responses, res)
  1615  
  1616  		// Update to see how much we still need
  1617  		requestAmount.Sub(requestAmount, amountCollected)
  1618  
  1619  		// here we check if we were able to collect all monies,
  1620  		// if not send an event to notify the plugins
  1621  		if party != types.NetworkParty {
  1622  			// no error possible here, we're just reloading the accounts to ensure the correct balance
  1623  			marginEvt.general, marginEvt.margin, marginEvt.bond, _ = e.getMTMPartyAccounts(party, settle.MarketID, asset)
  1624  			marginEvt.orderMargin, _ = e.GetAccountByID(e.accountID(marginEvt.marketID, party, marginEvt.asset, types.AccountTypeOrderMargin))
  1625  			totalInAccount := marginEvt.margin.Balance.Clone()
  1626  			if useGeneralAccountForMarginSearch(marginEvt.Party()) {
  1627  				totalInAccount.AddSum(marginEvt.general.Balance)
  1628  			}
  1629  			if marginEvt.bond != nil {
  1630  				totalInAccount.Add(totalInAccount, marginEvt.bond.Balance)
  1631  			}
  1632  
  1633  			if totalInAccount.LT(requestAmount) {
  1634  				delta := req.Amount.Sub(requestAmount, totalInAccount)
  1635  				e.log.Warn("loss socialization missing amount to be collected or used from insurance pool",
  1636  					logging.String("party-id", party),
  1637  					logging.BigUint("amount", delta),
  1638  					logging.String("market-id", settle.MarketID))
  1639  
  1640  				brokerEvts = append(brokerEvts,
  1641  					events.NewLossSocializationEvent(ctx, party, settle.MarketID, delta, false, now, lType))
  1642  			}
  1643  			marginEvts = append(marginEvts, marginEvt)
  1644  		} else {
  1645  			// we've used the insurance account as a margin account for the network
  1646  			// we have to update it to ensure we're aware of its balance
  1647  			settle, insurance, _ = e.getSystemAccounts(marketID, asset)
  1648  		}
  1649  	}
  1650  
  1651  	if len(brokerEvts) > 0 {
  1652  		e.broker.SendBatch(brokerEvts)
  1653  	}
  1654  	// we couldn't have reached this point without settlement account
  1655  	// it's also prudent to reset the insurance account... the network position
  1656  	// relies on its balance being accurate
  1657  	settle, insurance, _ = e.getSystemAccounts(marketID, asset)
  1658  	// if winidx is 0, this means we had now wind and loss, but may have some event which
  1659  	// needs to be propagated forward so we return now.
  1660  	if winidx == 0 {
  1661  		if !settle.Balance.IsZero() {
  1662  			e.log.Panic("No win transfers, settlement balance non-zero", logging.BigUint("settlement-balance", settle.Balance))
  1663  			return nil, nil, ErrSettlementBalanceNotZero
  1664  		}
  1665  		return marginEvts, responses, nil
  1666  	}
  1667  
  1668  	// now compare what's in the settlement account what we expect initially to redistribute.
  1669  	// if there's not enough we enter loss socialization
  1670  	distr := simpleDistributor{
  1671  		log:             e.log,
  1672  		marketID:        settle.MarketID,
  1673  		expectCollected: expCollected,
  1674  		collected:       settle.Balance,
  1675  		requests:        make([]request, 0, len(transfers)-winidx),
  1676  		ts:              now,
  1677  		lType:           lType,
  1678  	}
  1679  
  1680  	if distr.LossSocializationEnabled() {
  1681  		e.log.Warn("Entering loss socialization",
  1682  			logging.String("market-id", marketID),
  1683  			logging.String("asset", asset),
  1684  			logging.BigUint("expect-collected", expCollected),
  1685  			logging.BigUint("collected", settle.Balance))
  1686  		for _, evt := range transfers[winidx:] {
  1687  			transfer := evt.Transfer()
  1688  			if transfer != nil && transfer.Type == winType {
  1689  				distr.Add(evt.Transfer())
  1690  			}
  1691  		}
  1692  		if evts := distr.Run(ctx); len(evts) != 0 {
  1693  			e.broker.SendBatch(evts)
  1694  		}
  1695  	}
  1696  
  1697  	// then we process all the wins
  1698  	for _, evt := range transfers[winidx:] {
  1699  		transfer := evt.Transfer()
  1700  		party := evt.Party()
  1701  		marginEvt := &marginUpdate{
  1702  			MarketPosition: evt,
  1703  			asset:          asset,
  1704  			marketID:       settle.MarketID,
  1705  		}
  1706  		// no transfer needed if transfer is nil, just build the marginUpdate
  1707  		if party != types.NetworkParty {
  1708  			marginEvt.general, marginEvt.margin, marginEvt.bond, err = e.getMTMPartyAccounts(party, settle.MarketID, asset)
  1709  			if err != nil {
  1710  				e.log.Error("unable to get party account",
  1711  					logging.String("account-type", "margin"),
  1712  					logging.String("party-id", evt.Party()),
  1713  					logging.String("asset", asset),
  1714  					logging.String("market-id", settle.MarketID))
  1715  			}
  1716  			marginEvt.orderMargin, _ = e.GetAccountByID(e.accountID(marginEvt.marketID, party, marginEvt.asset, types.AccountTypeOrderMargin))
  1717  			if transfer == nil {
  1718  				marginEvts = append(marginEvts, marginEvt)
  1719  				continue
  1720  			}
  1721  		}
  1722  
  1723  		req, err := e.getTransferRequest(transfer, settle, insurance, marginEvt, true)
  1724  		if err != nil {
  1725  			e.log.Error(
  1726  				"Failed to build transfer request for event",
  1727  				logging.Error(err),
  1728  			)
  1729  			return nil, nil, err
  1730  		}
  1731  		if req == nil {
  1732  			// nil transfer encountered
  1733  			continue
  1734  		}
  1735  
  1736  		// set the amount (this can change the req.Amount value if we entered loss socialisation
  1737  		res, err := e.getLedgerEntries(ctx, req)
  1738  		if err != nil {
  1739  			e.log.Error(
  1740  				"Failed to transfer funds",
  1741  				logging.Error(err),
  1742  			)
  1743  			return nil, nil, err
  1744  		}
  1745  
  1746  		// update the to accounts now
  1747  		for _, bal := range res.Balances {
  1748  			if err := e.IncrementBalance(ctx, bal.Account.ID, bal.Balance); err != nil {
  1749  				e.log.Error(
  1750  					"Could not update the target account in transfer",
  1751  					logging.String("account-id", bal.Account.ID),
  1752  					logging.Error(err),
  1753  				)
  1754  				return nil, nil, err
  1755  			}
  1756  		}
  1757  		responses = append(responses, res)
  1758  		if party == types.NetworkParty {
  1759  			continue
  1760  		}
  1761  		// updating the accounts stored in the marginEvt
  1762  		// this can't return an error
  1763  		marginEvt.general, marginEvt.margin, marginEvt.bond, _ = e.getMTMPartyAccounts(party, settle.MarketID, asset)
  1764  
  1765  		marginEvts = append(marginEvts, marginEvt)
  1766  	}
  1767  
  1768  	if !settle.Balance.IsZero() {
  1769  		if round == nil || settle.Balance.GT(round) {
  1770  			e.log.Panic("Settlement balance non-zero at the end of MTM/funding settlement", logging.BigUint("settlement-balance", settle.Balance))
  1771  			return nil, nil, ErrSettlementBalanceNotZero
  1772  		}
  1773  		// non-zero balance, but within rounding margin
  1774  		req := &types.TransferRequest{
  1775  			FromAccount: []*types.Account{settle},
  1776  			ToAccount:   []*types.Account{insurance},
  1777  			Asset:       asset,
  1778  			Type:        types.TransferTypeClearAccount,
  1779  			Amount:      settle.Balance.Clone(),
  1780  		}
  1781  		ledgerEntries, err := e.getLedgerEntries(ctx, req)
  1782  		if err != nil {
  1783  			e.log.Panic("unable to redistribute settlement leftover funds", logging.Error(err))
  1784  		}
  1785  		for _, bal := range ledgerEntries.Balances {
  1786  			if err := e.IncrementBalance(ctx, bal.Account.ID, bal.Balance); err != nil {
  1787  				e.log.Error("Could not update the target account in transfer",
  1788  					logging.String("account-id", bal.Account.ID),
  1789  					logging.Error(err))
  1790  				return nil, nil, err
  1791  			}
  1792  		}
  1793  		responses = append(responses, ledgerEntries)
  1794  	}
  1795  	return marginEvts, responses, nil
  1796  }
  1797  
  1798  // GetPartyMargin will return the current margin for a given party.
  1799  func (e *Engine) GetPartyMargin(pos events.MarketPosition, asset, marketID string) (events.Margin, error) {
  1800  	if pos.Party() == types.NetworkParty {
  1801  		ins, err := e.GetMarketInsurancePoolAccount(marketID, asset)
  1802  		if err != nil {
  1803  			return nil, ErrAccountDoesNotExist
  1804  		}
  1805  		return marginUpdate{
  1806  			MarketPosition:  pos,
  1807  			margin:          ins,
  1808  			asset:           asset,
  1809  			marketID:        marketID,
  1810  			marginShortFall: num.UintZero(),
  1811  		}, nil
  1812  	}
  1813  	genID := e.accountID(noMarket, pos.Party(), asset, types.AccountTypeGeneral)
  1814  	marginID := e.accountID(marketID, pos.Party(), asset, types.AccountTypeMargin)
  1815  	orderMarginID := e.accountID(marketID, pos.Party(), asset, types.AccountTypeOrderMargin)
  1816  	bondID := e.accountID(marketID, pos.Party(), asset, types.AccountTypeBond)
  1817  	genAcc, err := e.GetAccountByID(genID)
  1818  	if err != nil {
  1819  		e.log.Error(
  1820  			"Party doesn't have a general account somehow?",
  1821  			logging.String("party-id", pos.Party()))
  1822  		return nil, ErrPartyAccountsMissing
  1823  	}
  1824  	marAcc, err := e.GetAccountByID(marginID)
  1825  	if err != nil {
  1826  		e.log.Error(
  1827  			"Party doesn't have a margin account somehow?",
  1828  			logging.String("party-id", pos.Party()),
  1829  			logging.String("market-id", marketID))
  1830  		return nil, ErrPartyAccountsMissing
  1831  	}
  1832  
  1833  	// can be nil for a party in cross margin mode
  1834  	orderMarAcc, _ := e.GetAccountByID(orderMarginID)
  1835  
  1836  	// do not check error,
  1837  	// not all parties have a bond account
  1838  	bondAcc, _ := e.GetAccountByID(bondID)
  1839  
  1840  	return marginUpdate{
  1841  		MarketPosition:  pos,
  1842  		margin:          marAcc,
  1843  		orderMargin:     orderMarAcc,
  1844  		general:         genAcc,
  1845  		lock:            nil,
  1846  		bond:            bondAcc,
  1847  		asset:           asset,
  1848  		marketID:        marketID,
  1849  		marginShortFall: num.UintZero(),
  1850  	}, nil
  1851  }
  1852  
  1853  // IsolatedMarginUpdate returns margin events for parties that don't meet their margin requirement (i.e. margin balance < maintenance).
  1854  func (e *Engine) IsolatedMarginUpdate(updates []events.Risk) []events.Margin {
  1855  	closed := make([]events.Margin, 0, len(updates))
  1856  	if len(updates) == 0 {
  1857  		return closed
  1858  	}
  1859  	for _, r := range updates {
  1860  		mevt := &marginUpdate{
  1861  			MarketPosition:  r,
  1862  			asset:           r.Asset(),
  1863  			marketID:        r.MarketID(),
  1864  			marginShortFall: num.UintZero(),
  1865  		}
  1866  		closed = append(closed, mevt)
  1867  	}
  1868  	return closed
  1869  }
  1870  
  1871  // MarginUpdate will run the margin updates over a set of risk events (margin updates).
  1872  func (e *Engine) MarginUpdate(ctx context.Context, marketID string, updates []events.Risk) ([]*types.LedgerMovement, []events.Margin, []events.Margin, error) {
  1873  	response := make([]*types.LedgerMovement, 0, len(updates))
  1874  	var (
  1875  		closed     = make([]events.Margin, 0, len(updates)/2) // half the cap, if we have more than that, the slice will double once, and will fit all updates anyway
  1876  		toPenalise = []events.Margin{}
  1877  		settle     = &types.Account{
  1878  			MarketID: marketID,
  1879  		}
  1880  	)
  1881  	// create "fake" settle account for market ID
  1882  	for _, update := range updates {
  1883  		if update.Party() == types.NetworkParty {
  1884  			// network party is ignored here
  1885  			continue
  1886  		}
  1887  		transfer := update.Transfer()
  1888  		// although this is mainly a duplicate event, we need to pass it to getTransferRequest
  1889  		mevt := &marginUpdate{
  1890  			MarketPosition:  update,
  1891  			asset:           update.Asset(),
  1892  			marketID:        update.MarketID(),
  1893  			marginShortFall: num.UintZero(),
  1894  		}
  1895  
  1896  		req, err := e.getTransferRequest(transfer, settle, nil, mevt, true)
  1897  		if err != nil {
  1898  			return response, closed, toPenalise, err
  1899  		}
  1900  
  1901  		// calculate the marginShortFall in case of a liquidityProvider
  1902  		if mevt.bond != nil && transfer.Amount.Amount.GT(mevt.general.Balance) {
  1903  			mevt.marginShortFall.Sub(transfer.Amount.Amount, mevt.general.Balance)
  1904  		}
  1905  
  1906  		res, err := e.getLedgerEntries(ctx, req)
  1907  		if err != nil {
  1908  			return response, closed, toPenalise, err
  1909  		}
  1910  		// we didn't manage to top up to even the minimum required system margin, close out party
  1911  		// we need to be careful with this, only apply this to transfer for low margin
  1912  		// the MinAmount in the transfer is always set to 0 but in 2 case:
  1913  		// - first when a new order is created, the MinAmount is the same than Amount, which is
  1914  		//   what's required to reach the InitialMargin level
  1915  		// - second when a party margin is under the MaintenanceLevel, the MinAmount is supposed
  1916  		//   to be at least to get back to the search level, and the amount will be enough to reach
  1917  		//   InitialMargin
  1918  		// In both case either the order will not be accepted, or the party will be closed
  1919  		if transfer.Type == types.TransferTypeMarginLow &&
  1920  			res.Balances[0].Account.Balance.LT(num.Sum(update.MarginBalance(), transfer.MinAmount)) {
  1921  			closed = append(closed, mevt)
  1922  		}
  1923  		// always add the event as well
  1924  		if !mevt.marginShortFall.IsZero() {
  1925  			// party not closed out, but could also not fulfill it's margin requirement
  1926  			// from it's general account we need to return this information so penalty can be
  1927  			// calculated an taken out from him.
  1928  			toPenalise = append(toPenalise, mevt)
  1929  		}
  1930  		response = append(response, res)
  1931  		for _, v := range res.Entries {
  1932  			// increment the to account
  1933  			if err := e.IncrementBalance(ctx, e.ADtoID(v.ToAccount), v.Amount); err != nil {
  1934  				e.log.Error(
  1935  					"Failed to increment balance for account",
  1936  					logging.String("asset", v.ToAccount.AssetID),
  1937  					logging.String("market", v.ToAccount.MarketID),
  1938  					logging.String("owner", v.ToAccount.Owner),
  1939  					logging.String("type", v.ToAccount.Type.String()),
  1940  					logging.BigUint("amount", v.Amount),
  1941  					logging.Error(err),
  1942  				)
  1943  			}
  1944  		}
  1945  	}
  1946  
  1947  	return response, closed, toPenalise, nil
  1948  }
  1949  
  1950  // RollbackMarginUpdateOnOrder moves funds from the margin to the general account.
  1951  func (e *Engine) RollbackMarginUpdateOnOrder(ctx context.Context, marketID string, assetID string, transfer *types.Transfer) (*types.LedgerMovement, error) {
  1952  	margin, err := e.GetAccountByID(e.accountID(marketID, transfer.Owner, assetID, types.AccountTypeMargin))
  1953  	if err != nil {
  1954  		e.log.Error(
  1955  			"Failed to get the margin party account",
  1956  			logging.String("owner-id", transfer.Owner),
  1957  			logging.String("market-id", marketID),
  1958  			logging.Error(err),
  1959  		)
  1960  		return nil, err
  1961  	}
  1962  	// we'll need this account for all transfer types anyway (settlements, margin-risk updates)
  1963  	general, err := e.GetAccountByID(e.accountID(noMarket, transfer.Owner, assetID, types.AccountTypeGeneral))
  1964  	if err != nil {
  1965  		e.log.Error(
  1966  			"Failed to get the general party account",
  1967  			logging.String("owner-id", transfer.Owner),
  1968  			logging.String("market-id", marketID),
  1969  			logging.Error(err),
  1970  		)
  1971  		return nil, err
  1972  	}
  1973  
  1974  	req := &types.TransferRequest{
  1975  		FromAccount: []*types.Account{
  1976  			margin,
  1977  		},
  1978  		ToAccount: []*types.Account{
  1979  			general,
  1980  		},
  1981  		Amount:    transfer.Amount.Amount.Clone(),
  1982  		MinAmount: num.UintZero(),
  1983  		Asset:     assetID,
  1984  		Type:      transfer.Type,
  1985  	}
  1986  	// @TODO we should be able to clone the min amount regardless
  1987  	if transfer.MinAmount != nil {
  1988  		req.MinAmount.Set(transfer.MinAmount)
  1989  	}
  1990  
  1991  	res, err := e.getLedgerEntries(ctx, req)
  1992  	if err != nil {
  1993  		return nil, err
  1994  	}
  1995  	for _, v := range res.Entries {
  1996  		// increment the to account
  1997  		if err := e.IncrementBalance(ctx, e.ADtoID(v.ToAccount), v.Amount); err != nil {
  1998  			e.log.Error(
  1999  				"Failed to increment balance for account",
  2000  				logging.String("asset", v.ToAccount.AssetID),
  2001  				logging.String("market", v.ToAccount.MarketID),
  2002  				logging.String("owner", v.ToAccount.Owner),
  2003  				logging.String("type", v.ToAccount.Type.String()),
  2004  				logging.BigUint("amount", v.Amount),
  2005  				logging.Error(err),
  2006  			)
  2007  		}
  2008  	}
  2009  
  2010  	return res, nil
  2011  }
  2012  
  2013  func (e *Engine) TransferFunds(
  2014  	ctx context.Context,
  2015  	transfers []*types.Transfer,
  2016  	accountTypes []types.AccountType,
  2017  	references []string,
  2018  	feeTransfers []*types.Transfer,
  2019  	feeTransfersAccountType []types.AccountType,
  2020  ) ([]*types.LedgerMovement, error) {
  2021  	if len(transfers) != len(accountTypes) || len(transfers) != len(references) {
  2022  		e.log.Panic("not the same amount of transfers, accounts types and references to process",
  2023  			logging.Int("transfers", len(transfers)),
  2024  			logging.Int("accounts-types", len(accountTypes)),
  2025  			logging.Int("reference", len(references)),
  2026  		)
  2027  	}
  2028  	if len(feeTransfers) != len(feeTransfersAccountType) {
  2029  		e.log.Panic("not the same amount of fee transfers and accounts types to process",
  2030  			logging.Int("fee-transfers", len(feeTransfers)),
  2031  			logging.Int("fee-accounts-types", len(feeTransfersAccountType)),
  2032  		)
  2033  	}
  2034  
  2035  	var (
  2036  		resps           = make([]*types.LedgerMovement, 0, len(transfers)+len(feeTransfers))
  2037  		err             error
  2038  		req             *types.TransferRequest
  2039  		allTransfers    = append(transfers, feeTransfers...)
  2040  		allAccountTypes = append(accountTypes, feeTransfersAccountType...)
  2041  	)
  2042  
  2043  	for i := range allTransfers {
  2044  		transfer, accType := allTransfers[i], allAccountTypes[i]
  2045  		switch allTransfers[i].Type {
  2046  		case types.TransferTypeInfrastructureFeePay:
  2047  			req, err = e.getTransferFundsFeesTransferRequest(ctx, transfer, accType)
  2048  		case types.TransferTypeTransferFundsDistribute,
  2049  			types.TransferTypeTransferFundsSend:
  2050  			req, err = e.getTransferFundsTransferRequest(ctx, transfer, accType)
  2051  		default:
  2052  			e.log.Panic("unsupported transfer type",
  2053  				logging.String("types", accType.String()))
  2054  		}
  2055  
  2056  		if err != nil {
  2057  			return nil, err
  2058  		}
  2059  
  2060  		res, err := e.getLedgerEntries(ctx, req)
  2061  		if err != nil {
  2062  			return nil, err
  2063  		}
  2064  
  2065  		for _, v := range res.Entries {
  2066  			// increment the to account
  2067  			if err := e.IncrementBalance(ctx, e.ADtoID(v.ToAccount), v.Amount); err != nil {
  2068  				e.log.Error(
  2069  					"Failed to increment balance for account",
  2070  					logging.String("asset", v.ToAccount.AssetID),
  2071  					logging.String("market", v.ToAccount.MarketID),
  2072  					logging.String("owner", v.ToAccount.Owner),
  2073  					logging.String("type", v.ToAccount.Type.String()),
  2074  					logging.BigUint("amount", v.Amount),
  2075  					logging.Error(err),
  2076  				)
  2077  			}
  2078  		}
  2079  
  2080  		resps = append(resps, res)
  2081  	}
  2082  
  2083  	return resps, nil
  2084  }
  2085  
  2086  func (e *Engine) GovernanceTransferFunds(
  2087  	ctx context.Context,
  2088  	transfers []*types.Transfer,
  2089  	accountTypes []types.AccountType,
  2090  	references []string,
  2091  ) ([]*types.LedgerMovement, error) {
  2092  	if len(transfers) != len(accountTypes) || len(transfers) != len(references) {
  2093  		e.log.Panic("not the same amount of transfers, accounts types and references to process",
  2094  			logging.Int("transfers", len(transfers)),
  2095  			logging.Int("accounts-types", len(accountTypes)),
  2096  			logging.Int("reference", len(references)),
  2097  		)
  2098  	}
  2099  
  2100  	var (
  2101  		resps = make([]*types.LedgerMovement, 0, len(transfers))
  2102  		err   error
  2103  		req   *types.TransferRequest
  2104  	)
  2105  
  2106  	for i := range transfers {
  2107  		transfer, accType := transfers[i], accountTypes[i]
  2108  		switch transfers[i].Type {
  2109  		case types.TransferTypeTransferFundsDistribute,
  2110  			types.TransferTypeTransferFundsSend:
  2111  			req, err = e.getGovernanceTransferFundsTransferRequest(ctx, transfer, accType)
  2112  
  2113  		default:
  2114  			e.log.Panic("unsupported transfer type",
  2115  				logging.String("types", accType.String()))
  2116  		}
  2117  
  2118  		if err != nil {
  2119  			return nil, err
  2120  		}
  2121  
  2122  		res, err := e.getLedgerEntries(ctx, req)
  2123  		if err != nil {
  2124  			return nil, err
  2125  		}
  2126  
  2127  		for _, v := range res.Entries {
  2128  			// increment the to account
  2129  			if err := e.IncrementBalance(ctx, e.ADtoID(v.ToAccount), v.Amount); err != nil {
  2130  				e.log.Error(
  2131  					"Failed to increment balance for account",
  2132  					logging.String("asset", v.ToAccount.AssetID),
  2133  					logging.String("market", v.ToAccount.MarketID),
  2134  					logging.String("owner", v.ToAccount.Owner),
  2135  					logging.String("type", v.ToAccount.Type.String()),
  2136  					logging.BigUint("amount", v.Amount),
  2137  					logging.Error(err),
  2138  				)
  2139  			}
  2140  		}
  2141  
  2142  		resps = append(resps, res)
  2143  	}
  2144  
  2145  	return resps, nil
  2146  }
  2147  
  2148  // BondUpdate is to be used for any bond account transfers.
  2149  // Update on new orders, updates on commitment changes, or on slashing.
  2150  func (e *Engine) BondUpdate(ctx context.Context, market string, transfer *types.Transfer) (*types.LedgerMovement, error) {
  2151  	req, err := e.getBondTransferRequest(transfer, market)
  2152  	if err != nil {
  2153  		return nil, err
  2154  	}
  2155  
  2156  	res, err := e.getLedgerEntries(ctx, req)
  2157  	if err != nil {
  2158  		return nil, err
  2159  	}
  2160  
  2161  	for _, v := range res.Entries {
  2162  		// increment the to account
  2163  		if err := e.IncrementBalance(ctx, e.ADtoID(v.ToAccount), v.Amount); err != nil {
  2164  			e.log.Error(
  2165  				"Failed to increment balance for account",
  2166  				logging.String("asset", v.ToAccount.AssetID),
  2167  				logging.String("market", v.ToAccount.MarketID),
  2168  				logging.String("owner", v.ToAccount.Owner),
  2169  				logging.String("type", v.ToAccount.Type.String()),
  2170  				logging.BigUint("amount", v.Amount),
  2171  				logging.Error(err),
  2172  			)
  2173  		}
  2174  	}
  2175  
  2176  	return res, nil
  2177  }
  2178  
  2179  func (e *Engine) RemoveBondAccount(partyID, marketID, asset string) error {
  2180  	bondID := e.accountID(marketID, partyID, asset, types.AccountTypeBond)
  2181  	bondAcc, ok := e.accs[bondID]
  2182  	if !ok {
  2183  		return ErrAccountDoesNotExist
  2184  	}
  2185  	if !bondAcc.Balance.IsZero() {
  2186  		e.log.Panic("attempting to delete a bond account with non-zero balance")
  2187  	}
  2188  	e.removeAccount(bondID)
  2189  	return nil
  2190  }
  2191  
  2192  // MarginUpdateOnOrder will run the margin updates over a set of risk events (margin updates).
  2193  func (e *Engine) MarginUpdateOnOrder(ctx context.Context, marketID string, update events.Risk) (*types.LedgerMovement, events.Margin, error) {
  2194  	// network party is ignored for margin stuff.
  2195  	if update.Party() == types.NetworkParty {
  2196  		return nil, nil, nil
  2197  	}
  2198  	// create "fake" settle account for market ID
  2199  	settle := &types.Account{
  2200  		MarketID: marketID,
  2201  	}
  2202  	transfer := update.Transfer()
  2203  	// although this is mainly a duplicate event, we need to pass it to getTransferRequest
  2204  	mevt := marginUpdate{
  2205  		MarketPosition:  update,
  2206  		asset:           update.Asset(),
  2207  		marketID:        update.MarketID(),
  2208  		marginShortFall: num.UintZero(),
  2209  	}
  2210  
  2211  	req, err := e.getTransferRequest(transfer, settle, nil, &mevt, true)
  2212  	if err != nil {
  2213  		return nil, nil, err
  2214  	}
  2215  
  2216  	// we do not have enough money to get to the minimum amount,
  2217  	// we return an error.
  2218  	if transfer.Type == types.TransferTypeMarginLow && num.Sum(mevt.GeneralBalance(), mevt.MarginBalance()).LT(transfer.MinAmount) {
  2219  		return nil, mevt, ErrMinAmountNotReached
  2220  	}
  2221  	if transfer.Type == types.TransferTypeOrderMarginLow && num.Sum(mevt.GeneralBalance(), mevt.OrderMarginBalance()).LT(transfer.MinAmount) {
  2222  		return nil, mevt, ErrMinAmountNotReached
  2223  	}
  2224  	if mevt.bond != nil && transfer.Amount.Amount.GT(mevt.general.Balance) {
  2225  		// this is a liquidity provider but it did not have enough funds to
  2226  		// pay from the general account, we'll have to penalize later on
  2227  		mevt.marginShortFall.Sub(transfer.Amount.Amount, mevt.general.Balance)
  2228  	}
  2229  
  2230  	// from here we know there's enough money,
  2231  	// let get the ledger entries, return the transfers
  2232  
  2233  	res, err := e.getLedgerEntries(ctx, req)
  2234  	if err != nil {
  2235  		return nil, nil, err
  2236  	}
  2237  	for _, v := range res.Entries {
  2238  		// increment the to account
  2239  		if err := e.IncrementBalance(ctx, e.ADtoID(v.ToAccount), v.Amount); err != nil {
  2240  			e.log.Error(
  2241  				"Failed to increment balance for account",
  2242  				logging.String("asset", v.ToAccount.AssetID),
  2243  				logging.String("market", v.ToAccount.MarketID),
  2244  				logging.String("owner", v.ToAccount.Owner),
  2245  				logging.String("type", v.ToAccount.Type.String()),
  2246  				logging.BigUint("amount", v.Amount),
  2247  				logging.Error(err),
  2248  			)
  2249  		}
  2250  	}
  2251  
  2252  	if !mevt.marginShortFall.IsZero() {
  2253  		return res, mevt, nil
  2254  	}
  2255  	return res, nil, nil
  2256  }
  2257  
  2258  func (e *Engine) getFeeTransferRequest(
  2259  	ctx context.Context,
  2260  	t *types.Transfer,
  2261  	makerFee, infraFee, liquiFee *types.Account,
  2262  	marketID, assetID string,
  2263  ) (*types.TransferRequest, error) {
  2264  	getAccountLogError := func(marketID, owner string, accountType vega.AccountType) (*types.Account, error) {
  2265  		if owner == types.NetworkParty {
  2266  			return e.GetMarketInsurancePoolAccount(marketID, assetID)
  2267  		}
  2268  		acc, err := e.GetAccountByID(e.accountID(marketID, owner, assetID, accountType))
  2269  		if err != nil {
  2270  			e.log.Error(
  2271  				fmt.Sprintf("Failed to get the %q %q account", owner, accountType),
  2272  				logging.String("owner-id", t.Owner),
  2273  				logging.String("market-id", marketID),
  2274  				logging.Error(err),
  2275  			)
  2276  			return nil, err
  2277  		}
  2278  
  2279  		return acc, nil
  2280  	}
  2281  
  2282  	partyLiquidityFeeAccount := func() (*types.Account, error) {
  2283  		if t.Owner == types.NetworkParty {
  2284  			return e.GetMarketInsurancePoolAccount(marketID, assetID)
  2285  		}
  2286  		return e.GetOrCreatePartyLiquidityFeeAccount(ctx, t.Owner, marketID, assetID)
  2287  	}
  2288  
  2289  	bonusDistributionAccount := func() (*types.Account, error) {
  2290  		return e.GetOrCreateLiquidityFeesBonusDistributionAccount(ctx, marketID, assetID)
  2291  	}
  2292  
  2293  	marginAccount := func() (*types.Account, error) {
  2294  		return getAccountLogError(marketID, t.Owner, types.AccountTypeMargin)
  2295  	}
  2296  
  2297  	orderMarginAccount := func() *types.Account {
  2298  		acc, _ := e.GetAccountByID(e.accountID(marketID, t.Owner, assetID, types.AccountTypeOrderMargin))
  2299  		return acc
  2300  	}
  2301  
  2302  	referralPendingRewardAccount := func() (*types.Account, error) {
  2303  		return getAccountLogError(noMarket, systemOwner, types.AccountTypePendingFeeReferralReward)
  2304  	}
  2305  
  2306  	var (
  2307  		general *types.Account
  2308  		err     error
  2309  	)
  2310  	networkTreausry, err := e.GetNetworkTreasuryAccount(assetID)
  2311  	if err != nil {
  2312  		return nil, err
  2313  	}
  2314  	buyBackAccount := e.getOrCreateBuyBackFeesAccount(ctx, assetID)
  2315  
  2316  	if t.Owner == types.NetworkParty {
  2317  		general, err = e.GetMarketInsurancePoolAccount(marketID, assetID)
  2318  		if err != nil {
  2319  			return nil, fmt.Errorf("no insurance pool for the market %w", err)
  2320  		}
  2321  	} else {
  2322  		general, err = e.GetAccountByID(e.accountID(noMarket, t.Owner, assetID, types.AccountTypeGeneral))
  2323  		if err != nil {
  2324  			generalID, err := e.CreatePartyGeneralAccount(ctx, t.Owner, assetID)
  2325  			if err != nil {
  2326  				return nil, err
  2327  			}
  2328  			general, err = e.GetAccountByID(generalID)
  2329  			if err != nil {
  2330  				return nil, err
  2331  			}
  2332  		}
  2333  	}
  2334  
  2335  	treq := &types.TransferRequest{
  2336  		Amount:    t.Amount.Amount.Clone(),
  2337  		MinAmount: t.Amount.Amount.Clone(),
  2338  		Asset:     assetID,
  2339  		Type:      t.Type,
  2340  	}
  2341  
  2342  	switch t.Type {
  2343  	case types.TransferTypeFeeReferrerRewardPay:
  2344  		margin, err := marginAccount()
  2345  		if err != nil {
  2346  			return nil, err
  2347  		}
  2348  		orderMargin := orderMarginAccount()
  2349  
  2350  		pendingRewardAccount, err := referralPendingRewardAccount()
  2351  		if err != nil {
  2352  			return nil, err
  2353  		}
  2354  		treq.FromAccount = []*types.Account{general, margin}
  2355  		if orderMargin != nil {
  2356  			treq.FromAccount = append(treq.FromAccount, orderMargin)
  2357  		}
  2358  		treq.ToAccount = []*types.Account{pendingRewardAccount}
  2359  	case types.TransferTypeFeeReferrerRewardDistribute:
  2360  		pendingRewardAccount, err := referralPendingRewardAccount()
  2361  		if err != nil {
  2362  			return nil, err
  2363  		}
  2364  		treq.FromAccount = []*types.Account{pendingRewardAccount}
  2365  		treq.ToAccount = []*types.Account{general}
  2366  	case types.TransferTypeInfrastructureFeePay:
  2367  		margin, err := marginAccount()
  2368  		if err != nil {
  2369  			return nil, err
  2370  		}
  2371  		orderMargin := orderMarginAccount()
  2372  		treq.FromAccount = []*types.Account{general, margin}
  2373  		if orderMargin != nil {
  2374  			treq.FromAccount = append(treq.FromAccount, orderMargin)
  2375  		}
  2376  		treq.ToAccount = []*types.Account{infraFee}
  2377  	case types.TransferTypeTreasuryPay:
  2378  		margin, err := marginAccount()
  2379  		if err != nil {
  2380  			return nil, err
  2381  		}
  2382  		orderMargin := orderMarginAccount()
  2383  		treq.FromAccount = []*types.Account{general, margin}
  2384  		if orderMargin != nil {
  2385  			treq.FromAccount = append(treq.FromAccount, orderMargin)
  2386  		}
  2387  		treq.ToAccount = []*types.Account{networkTreausry}
  2388  	case types.TransferTypeBuyBackFeePay:
  2389  		margin, err := marginAccount()
  2390  		if err != nil {
  2391  			return nil, err
  2392  		}
  2393  		orderMargin := orderMarginAccount()
  2394  		treq.FromAccount = []*types.Account{general, margin}
  2395  		if orderMargin != nil {
  2396  			treq.FromAccount = append(treq.FromAccount, orderMargin)
  2397  		}
  2398  		treq.ToAccount = []*types.Account{buyBackAccount}
  2399  	case types.TransferTypeInfrastructureFeeDistribute:
  2400  		treq.FromAccount = []*types.Account{infraFee}
  2401  		treq.ToAccount = []*types.Account{general}
  2402  	case types.TransferTypeLiquidityFeePay:
  2403  		margin, err := marginAccount()
  2404  		if err != nil {
  2405  			return nil, err
  2406  		}
  2407  		orderMargin := orderMarginAccount()
  2408  		treq.FromAccount = []*types.Account{general, margin}
  2409  		if orderMargin != nil {
  2410  			treq.FromAccount = append(treq.FromAccount, orderMargin)
  2411  		}
  2412  		treq.ToAccount = []*types.Account{liquiFee}
  2413  	case types.TransferTypeLiquidityFeeDistribute:
  2414  		treq.FromAccount = []*types.Account{liquiFee}
  2415  		treq.ToAccount = []*types.Account{general}
  2416  	case types.TransferTypeMakerFeePay:
  2417  		margin, err := marginAccount()
  2418  		if err != nil {
  2419  			return nil, err
  2420  		}
  2421  		orderMargin := orderMarginAccount()
  2422  		treq.FromAccount = []*types.Account{general, margin}
  2423  		if orderMargin != nil {
  2424  			treq.FromAccount = append(treq.FromAccount, orderMargin)
  2425  		}
  2426  		treq.ToAccount = []*types.Account{makerFee}
  2427  	case types.TransferTypeMakerFeeReceive:
  2428  		treq.FromAccount = []*types.Account{makerFee}
  2429  		treq.ToAccount = []*types.Account{general}
  2430  	case types.TransferTypeHighMakerRebatePay:
  2431  		margin, err := marginAccount()
  2432  		if err != nil {
  2433  			return nil, err
  2434  		}
  2435  		orderMargin := orderMarginAccount()
  2436  		treq.FromAccount = []*types.Account{general, margin}
  2437  		if orderMargin != nil {
  2438  			treq.FromAccount = append(treq.FromAccount, orderMargin)
  2439  		}
  2440  		treq.ToAccount = []*types.Account{makerFee}
  2441  	case types.TransferTypeHighMakerRebateReceive:
  2442  		treq.FromAccount = []*types.Account{makerFee}
  2443  		treq.ToAccount = []*types.Account{general}
  2444  		return treq, nil
  2445  	case types.TransferTypeLiquidityFeeAllocate:
  2446  		partyLiquidityFee, err := partyLiquidityFeeAccount()
  2447  		if err != nil {
  2448  			return nil, err
  2449  		}
  2450  
  2451  		treq.FromAccount = []*types.Account{liquiFee}
  2452  		treq.ToAccount = []*types.Account{partyLiquidityFee}
  2453  	case types.TransferTypeLiquidityFeeNetDistribute:
  2454  		partyLiquidityFee, err := partyLiquidityFeeAccount()
  2455  		if err != nil {
  2456  			return nil, err
  2457  		}
  2458  
  2459  		treq.FromAccount = []*types.Account{partyLiquidityFee}
  2460  		treq.ToAccount = []*types.Account{general}
  2461  	case types.TransferTypeLiquidityFeeUnpaidCollect:
  2462  		partyLiquidityFee, err := partyLiquidityFeeAccount()
  2463  		if err != nil {
  2464  			return nil, err
  2465  		}
  2466  		bonusDistribution, err := bonusDistributionAccount()
  2467  		if err != nil {
  2468  			return nil, err
  2469  		}
  2470  
  2471  		treq.FromAccount = []*types.Account{partyLiquidityFee}
  2472  		treq.ToAccount = []*types.Account{bonusDistribution}
  2473  	case types.TransferTypeSlaPerformanceBonusDistribute:
  2474  		bonusDistribution, err := bonusDistributionAccount()
  2475  		if err != nil {
  2476  			return nil, err
  2477  		}
  2478  
  2479  		treq.FromAccount = []*types.Account{bonusDistribution}
  2480  		treq.ToAccount = []*types.Account{general}
  2481  	case types.TransferTypeSLAPenaltyLpFeeApply:
  2482  		partyLiquidityFee, err := partyLiquidityFeeAccount()
  2483  		if err != nil {
  2484  			return nil, err
  2485  		}
  2486  
  2487  		insurancePool, err := e.GetMarketInsurancePoolAccount(marketID, assetID)
  2488  		if err != nil {
  2489  			return nil, err
  2490  		}
  2491  
  2492  		treq.FromAccount = []*types.Account{partyLiquidityFee}
  2493  		treq.ToAccount = []*types.Account{insurancePool}
  2494  	default:
  2495  		return nil, ErrInvalidTransferTypeForFeeRequest
  2496  	}
  2497  	// we may be moving funds from the insurance pool, we cannot have more than 1 from account in that case
  2498  	// because once the insurance pool is drained, and a copy of the same account without the updated balance
  2499  	// sits in the FromAccount slice, we are magically doubling the available insurance pool funds.
  2500  	if len(treq.FromAccount) > 0 && treq.FromAccount[0].Type == types.AccountTypeInsurance {
  2501  		treq.FromAccount = treq.FromAccount[:1] // only the first account should be present
  2502  	}
  2503  	return treq, nil
  2504  }
  2505  
  2506  func (e *Engine) getBondTransferRequest(t *types.Transfer, market string) (*types.TransferRequest, error) {
  2507  	bond, err := e.GetAccountByID(e.accountID(market, t.Owner, t.Amount.Asset, types.AccountTypeBond))
  2508  	if err != nil {
  2509  		e.log.Error(
  2510  			"Failed to get the margin party account",
  2511  			logging.String("owner-id", t.Owner),
  2512  			logging.String("market-id", market),
  2513  			logging.Error(err),
  2514  		)
  2515  		return nil, err
  2516  	}
  2517  
  2518  	// we'll need this account for all transfer types anyway (settlements, margin-risk updates)
  2519  	general, err := e.GetAccountByID(e.accountID(noMarket, t.Owner, t.Amount.Asset, types.AccountTypeGeneral))
  2520  	if err != nil {
  2521  		e.log.Error(
  2522  			"Failed to get the general party account",
  2523  			logging.String("owner-id", t.Owner),
  2524  			logging.String("market-id", market),
  2525  			logging.Error(err),
  2526  		)
  2527  		return nil, err
  2528  	}
  2529  
  2530  	// we'll need this account for all transfer types anyway (settlements, margin-risk updates)
  2531  	insurancePool, err := e.GetAccountByID(e.accountID(market, systemOwner, t.Amount.Asset, types.AccountTypeInsurance))
  2532  	if err != nil {
  2533  		e.log.Error(
  2534  			"Failed to get the insurance pool account",
  2535  			logging.String("owner-id", t.Owner),
  2536  			logging.String("market-id", market),
  2537  			logging.Error(err),
  2538  		)
  2539  		return nil, err
  2540  	}
  2541  
  2542  	treq := &types.TransferRequest{
  2543  		Amount:    t.Amount.Amount.Clone(),
  2544  		MinAmount: t.Amount.Amount.Clone(),
  2545  		Asset:     t.Amount.Asset,
  2546  		Type:      t.Type,
  2547  	}
  2548  
  2549  	switch t.Type {
  2550  	case types.TransferTypeBondLow:
  2551  		// do we have enough in the general account to make the transfer?
  2552  		if !t.Amount.Amount.IsZero() && general.Balance.LT(t.Amount.Amount) {
  2553  			return nil, errors.New("not enough collateral in general account")
  2554  		}
  2555  		treq.FromAccount = []*types.Account{general}
  2556  		treq.ToAccount = []*types.Account{bond}
  2557  		return treq, nil
  2558  	case types.TransferTypeBondHigh:
  2559  		treq.FromAccount = []*types.Account{bond}
  2560  		treq.ToAccount = []*types.Account{general}
  2561  		return treq, nil
  2562  	case types.TransferTypeBondSlashing, types.TransferTypeSLAPenaltyBondApply:
  2563  		treq.FromAccount = []*types.Account{bond}
  2564  		// it's possible the bond account is insufficient, and falling back to margin balance
  2565  		// won't cause a close-out
  2566  		if marginAcc, err := e.GetAccountByID(e.accountID(market, t.Owner, t.Amount.Asset, types.AccountTypeMargin)); err == nil {
  2567  			treq.FromAccount = append(treq.FromAccount, marginAcc)
  2568  		}
  2569  		treq.ToAccount = []*types.Account{insurancePool}
  2570  		return treq, nil
  2571  	default:
  2572  		return nil, errors.New("unsupported transfer type for bond account")
  2573  	}
  2574  }
  2575  
  2576  // BondUpdate is to be used for any bond account transfers in a spot market.
  2577  // Update on new orders, updates on commitment changes, or on slashing.
  2578  func (e *Engine) BondSpotUpdate(ctx context.Context, market string, transfer *types.Transfer) (*types.LedgerMovement, error) {
  2579  	req, err := e.getBondSpotTransferRequest(transfer, market)
  2580  	if err != nil {
  2581  		return nil, err
  2582  	}
  2583  
  2584  	res, err := e.getLedgerEntries(ctx, req)
  2585  	if err != nil {
  2586  		return nil, err
  2587  	}
  2588  
  2589  	for _, v := range res.Entries {
  2590  		// Increment the to account.
  2591  		if err := e.IncrementBalance(ctx, e.ADtoID(v.ToAccount), v.Amount); err != nil {
  2592  			e.log.Error(
  2593  				"Failed to increment balance for account",
  2594  				logging.String("asset", v.ToAccount.AssetID),
  2595  				logging.String("market", v.ToAccount.MarketID),
  2596  				logging.String("owner", v.ToAccount.Owner),
  2597  				logging.String("type", v.ToAccount.Type.String()),
  2598  				logging.BigUint("amount", v.Amount),
  2599  				logging.Error(err),
  2600  			)
  2601  		}
  2602  	}
  2603  
  2604  	return res, nil
  2605  }
  2606  
  2607  func (e *Engine) getBondSpotTransferRequest(t *types.Transfer, market string) (*types.TransferRequest, error) {
  2608  	bond, err := e.GetAccountByID(e.accountID(market, t.Owner, t.Amount.Asset, types.AccountTypeBond))
  2609  	if err != nil {
  2610  		e.log.Error(
  2611  			"Failed to get the margin party account",
  2612  			logging.String("owner-id", t.Owner),
  2613  			logging.String("market-id", market),
  2614  			logging.Error(err),
  2615  		)
  2616  		return nil, err
  2617  	}
  2618  
  2619  	// We'll need this account for all transfer types anyway (settlements, margin-risk updates).
  2620  	general, err := e.GetAccountByID(e.accountID(noMarket, t.Owner, t.Amount.Asset, types.AccountTypeGeneral))
  2621  	if err != nil {
  2622  		e.log.Error(
  2623  			"Failed to get the general party account",
  2624  			logging.String("owner-id", t.Owner),
  2625  			logging.String("market-id", market),
  2626  			logging.Error(err),
  2627  		)
  2628  		return nil, err
  2629  	}
  2630  
  2631  	// We'll need this account for all transfer types anyway (settlements, margin-risk updates).
  2632  	networkTreasury, err := e.GetNetworkTreasuryAccount(t.Amount.Asset)
  2633  	if err != nil {
  2634  		e.log.Error(
  2635  			"Failed to get the network treasury account",
  2636  			logging.String("asset", t.Amount.Asset),
  2637  			logging.Error(err),
  2638  		)
  2639  		return nil, err
  2640  	}
  2641  
  2642  	treq := &types.TransferRequest{
  2643  		Amount:    t.Amount.Amount.Clone(),
  2644  		MinAmount: t.Amount.Amount.Clone(),
  2645  		Asset:     t.Amount.Asset,
  2646  		Type:      t.Type,
  2647  	}
  2648  
  2649  	switch t.Type {
  2650  	case types.TransferTypeBondLow:
  2651  		// Check that there is enough in the general account to make the transfer.
  2652  		if !t.Amount.Amount.IsZero() && general.Balance.LT(t.Amount.Amount) {
  2653  			return nil, errors.New("not enough collateral in general account")
  2654  		}
  2655  		treq.FromAccount = []*types.Account{general}
  2656  		treq.ToAccount = []*types.Account{bond}
  2657  		return treq, nil
  2658  	case types.TransferTypeBondHigh:
  2659  		treq.FromAccount = []*types.Account{bond}
  2660  		treq.ToAccount = []*types.Account{general}
  2661  		return treq, nil
  2662  	case types.TransferTypeBondSlashing, types.TransferTypeSLAPenaltyBondApply:
  2663  		treq.FromAccount = []*types.Account{bond}
  2664  		treq.ToAccount = []*types.Account{networkTreasury}
  2665  		return treq, nil
  2666  	default:
  2667  		return nil, errors.New("unsupported transfer type for bond account")
  2668  	}
  2669  }
  2670  
  2671  func (e *Engine) getGovernanceTransferFundsTransferRequest(ctx context.Context, t *types.Transfer, accountType types.AccountType) (*types.TransferRequest, error) {
  2672  	var (
  2673  		fromAcc, toAcc *types.Account
  2674  		err            error
  2675  	)
  2676  	switch t.Type {
  2677  	case types.TransferTypeTransferFundsSend:
  2678  		// as of now only general account are supported.
  2679  		// soon we'll have some kind of staking account lock as well.
  2680  		switch accountType {
  2681  		case types.AccountTypeGeneral:
  2682  			fromAcc, err = e.GetPartyGeneralAccount(t.Owner, t.Amount.Asset)
  2683  			if err != nil {
  2684  				return nil, fmt.Errorf("account does not exists: %v, %v, %v",
  2685  					accountType, t.Owner, t.Amount.Asset,
  2686  				)
  2687  			}
  2688  
  2689  			// we always pay onto the pending transfers accounts
  2690  			toAcc = e.GetPendingTransfersAccount(t.Amount.Asset)
  2691  
  2692  		case types.AccountTypeGlobalReward:
  2693  			fromAcc, err = e.GetGlobalRewardAccount(t.Amount.Asset)
  2694  			if err != nil {
  2695  				return nil, fmt.Errorf("account does not exists: %v, %v, %v",
  2696  					accountType, t.Owner, t.Amount.Asset,
  2697  				)
  2698  			}
  2699  
  2700  			// we always pay onto the pending transfers accounts
  2701  			toAcc = e.GetPendingTransfersAccount(t.Amount.Asset)
  2702  
  2703  		case types.AccountTypeNetworkTreasury:
  2704  			fromAcc, err = e.GetNetworkTreasuryAccount(t.Amount.Asset)
  2705  			if err != nil {
  2706  				return nil, fmt.Errorf("account does not exists: %v, %v, %v",
  2707  					accountType, t.Owner, t.Amount.Asset,
  2708  				)
  2709  			}
  2710  			// we always pay onto the pending transfers accounts
  2711  			toAcc = e.GetPendingTransfersAccount(t.Amount.Asset)
  2712  
  2713  		case types.AccountTypeGlobalInsurance:
  2714  			fromAcc, err = e.GetGlobalInsuranceAccount(t.Amount.Asset)
  2715  			if err != nil {
  2716  				return nil, fmt.Errorf("account does not exists: %v, %v, %v",
  2717  					accountType, t.Owner, t.Amount.Asset,
  2718  				)
  2719  			}
  2720  			// we always pay onto the pending transfers accounts
  2721  			toAcc = e.GetPendingTransfersAccount(t.Amount.Asset)
  2722  
  2723  		case types.AccountTypeInsurance:
  2724  			fromAcc, err = e.GetMarketInsurancePoolAccount(t.Market, t.Amount.Asset)
  2725  			if err != nil {
  2726  				return nil, fmt.Errorf("account does not exists: %v, %v, %v",
  2727  					accountType, t.Owner, t.Amount.Asset,
  2728  				)
  2729  			}
  2730  			// we always pay onto the pending transfers accounts
  2731  			toAcc = e.GetPendingTransfersAccount(t.Amount.Asset)
  2732  
  2733  		default:
  2734  			return nil, fmt.Errorf("unsupported from account for TransferFunds: %v", accountType.String())
  2735  		}
  2736  
  2737  	case types.TransferTypeTransferFundsDistribute:
  2738  		// as of now we support only another general account or a reward
  2739  		// pool
  2740  		switch accountType {
  2741  		// this account could not exists, we would need to create it then
  2742  		case types.AccountTypeGeneral:
  2743  			toAcc, err = e.GetPartyGeneralAccount(t.Owner, t.Amount.Asset)
  2744  			if err != nil {
  2745  				// account does not exists, let's just create it
  2746  				id, err := e.CreatePartyGeneralAccount(ctx, t.Owner, t.Amount.Asset)
  2747  				if err != nil {
  2748  					return nil, err
  2749  				}
  2750  				toAcc, err = e.GetAccountByID(id)
  2751  				if err != nil {
  2752  					// shouldn't happen, we just created it...
  2753  					return nil, err
  2754  				}
  2755  			}
  2756  
  2757  			// this could not exists as well, let's just create in this case
  2758  		case types.AccountTypeGlobalReward, types.AccountTypeLPFeeReward, types.AccountTypeMakerReceivedFeeReward,
  2759  			types.AccountTypeMakerPaidFeeReward, types.AccountTypeMarketProposerReward, types.AccountTypeAverageNotionalReward,
  2760  			types.AccountTypeRelativeReturnReward, types.AccountTypeReturnVolatilityReward, types.AccountTypeRealisedReturnReward,
  2761  			types.AccountTypeValidatorRankingReward, types.AccountTypeEligibleEntitiesReward, types.AccountTypeBuyBackFees:
  2762  			market := noMarket
  2763  			if len(t.Market) > 0 {
  2764  				market = t.Market
  2765  			}
  2766  			toAcc, err = e.GetOrCreateRewardAccount(ctx, t.Amount.Asset, market, accountType)
  2767  			if err != nil {
  2768  				// shouldn't happen, we just created it...
  2769  				return nil, err
  2770  			}
  2771  
  2772  		case types.AccountTypeNetworkTreasury:
  2773  			toAcc, err = e.GetNetworkTreasuryAccount(t.Amount.Asset)
  2774  			if err != nil {
  2775  				return nil, err
  2776  			}
  2777  
  2778  		case types.AccountTypeGlobalInsurance:
  2779  			toAcc, err = e.GetGlobalInsuranceAccount(t.Amount.Asset)
  2780  			if err != nil {
  2781  				return nil, err
  2782  			}
  2783  
  2784  		case types.AccountTypeInsurance:
  2785  			toAcc, err = e.GetMarketInsurancePoolAccount(t.Market, t.Amount.Asset)
  2786  			if err != nil {
  2787  				return nil, fmt.Errorf("account does not exists: %v, %v, %v",
  2788  					accountType, t.Amount.Asset, t.Market,
  2789  				)
  2790  			}
  2791  
  2792  		default:
  2793  			return nil, fmt.Errorf("unsupported to account for TransferFunds: %v", accountType.String())
  2794  		}
  2795  
  2796  		// from account will always be the pending for transfers
  2797  		fromAcc = e.GetPendingTransfersAccount(t.Amount.Asset)
  2798  
  2799  	default:
  2800  		return nil, fmt.Errorf("unsupported transfer type for TransferFund: %v", t.Type.String())
  2801  	}
  2802  
  2803  	// now we got all relevant accounts, we can build our request
  2804  
  2805  	return &types.TransferRequest{
  2806  		FromAccount: []*types.Account{fromAcc},
  2807  		ToAccount:   []*types.Account{toAcc},
  2808  		Amount:      t.Amount.Amount.Clone(),
  2809  		MinAmount:   t.Amount.Amount.Clone(),
  2810  		Asset:       t.Amount.Asset,
  2811  		Type:        t.Type,
  2812  		TransferID:  t.TransferID,
  2813  	}, nil
  2814  }
  2815  
  2816  func (e *Engine) getTransferFundsTransferRequest(ctx context.Context, t *types.Transfer, accountType types.AccountType) (*types.TransferRequest, error) {
  2817  	var (
  2818  		fromAcc, toAcc *types.Account
  2819  		err            error
  2820  	)
  2821  	switch t.Type {
  2822  	case types.TransferTypeTransferFundsSend:
  2823  		switch accountType {
  2824  		case types.AccountTypeGeneral:
  2825  			fromAcc, err = e.GetPartyGeneralAccount(t.Owner, t.Amount.Asset)
  2826  			if err != nil {
  2827  				return nil, fmt.Errorf("account does not exists: %v, %v, %v",
  2828  					accountType, t.Owner, t.Amount.Asset,
  2829  				)
  2830  			}
  2831  
  2832  			// we always pay onto the pending transfers accounts
  2833  			toAcc = e.GetPendingTransfersAccount(t.Amount.Asset)
  2834  
  2835  		case types.AccountTypeLockedForStaking:
  2836  			fromAcc, err = e.GetPartyLockedForStaking(t.Owner, t.Amount.Asset)
  2837  			if err != nil {
  2838  				return nil, fmt.Errorf("account does not exists: %v, %v, %v",
  2839  					accountType, t.Owner, t.Amount.Asset,
  2840  				)
  2841  			}
  2842  
  2843  			// we always pay onto the pending transfers accounts
  2844  			toAcc = e.GetPendingTransfersAccount(t.Amount.Asset)
  2845  
  2846  		case types.AccountTypeVestedRewards:
  2847  			fromAcc = e.GetOrCreatePartyVestedRewardAccount(ctx, t.Owner, t.Amount.Asset)
  2848  			// we always pay onto the pending transfers accounts
  2849  			toAcc = e.GetPendingTransfersAccount(t.Amount.Asset)
  2850  
  2851  		default:
  2852  			return nil, fmt.Errorf("unsupported from account for TransferFunds: %v", accountType.String())
  2853  		}
  2854  
  2855  	case types.TransferTypeTransferFundsDistribute:
  2856  		// as of now we support only another general account or a reward
  2857  		// pool
  2858  		switch accountType {
  2859  		// this account could not exists, we would need to create it then
  2860  		case types.AccountTypeGeneral:
  2861  			toAcc, err = e.GetPartyGeneralAccount(t.Owner, t.Amount.Asset)
  2862  			if err != nil {
  2863  				// account does not exists, let's just create it
  2864  				id, err := e.CreatePartyGeneralAccount(ctx, t.Owner, t.Amount.Asset)
  2865  				if err != nil {
  2866  					return nil, err
  2867  				}
  2868  				toAcc, err = e.GetAccountByID(id)
  2869  				if err != nil {
  2870  					// shouldn't happen, we just created it...
  2871  					return nil, err
  2872  				}
  2873  			}
  2874  
  2875  		case types.AccountTypeLockedForStaking:
  2876  			toAcc, err = e.GetPartyLockedForStaking(t.Owner, t.Amount.Asset)
  2877  			if err != nil {
  2878  				// account does not exists, let's just create it
  2879  				id, err := e.CreatePartyLockedForStakingAccount(ctx, t.Owner, t.Amount.Asset)
  2880  				if err != nil {
  2881  					return nil, err
  2882  				}
  2883  				toAcc, err = e.GetAccountByID(id)
  2884  				if err != nil {
  2885  					// shouldn't happen, we just created it...
  2886  					return nil, err
  2887  				}
  2888  			}
  2889  
  2890  		// this could not exists as well, let's just create in this case
  2891  		case types.AccountTypeGlobalReward, types.AccountTypeLPFeeReward, types.AccountTypeMakerReceivedFeeReward, types.AccountTypeNetworkTreasury,
  2892  			types.AccountTypeMakerPaidFeeReward, types.AccountTypeMarketProposerReward, types.AccountTypeAverageNotionalReward,
  2893  			types.AccountTypeRelativeReturnReward, types.AccountTypeReturnVolatilityReward, types.AccountTypeRealisedReturnReward,
  2894  			types.AccountTypeValidatorRankingReward, types.AccountTypeEligibleEntitiesReward, types.AccountTypeBuyBackFees:
  2895  			market := noMarket
  2896  			if len(t.Market) > 0 {
  2897  				market = t.Market
  2898  			}
  2899  			toAcc, err = e.GetOrCreateRewardAccount(ctx, t.Amount.Asset, market, accountType)
  2900  			if err != nil {
  2901  				// shouldn't happen, we just created it...
  2902  				return nil, err
  2903  			}
  2904  		default:
  2905  			return nil, fmt.Errorf("unsupported to account for TransferFunds: %v", accountType.String())
  2906  		}
  2907  
  2908  		// from account will always be the pending for transfers
  2909  		fromAcc = e.GetPendingTransfersAccount(t.Amount.Asset)
  2910  
  2911  	default:
  2912  		return nil, fmt.Errorf("unsupported transfer type for TransferFund: %v", t.Type.String())
  2913  	}
  2914  
  2915  	// now we got all relevant accounts, we can build our request
  2916  
  2917  	return &types.TransferRequest{
  2918  		FromAccount: []*types.Account{fromAcc},
  2919  		ToAccount:   []*types.Account{toAcc},
  2920  		Amount:      t.Amount.Amount.Clone(),
  2921  		MinAmount:   t.Amount.Amount.Clone(),
  2922  		Asset:       t.Amount.Asset,
  2923  		Type:        t.Type,
  2924  		TransferID:  t.TransferID,
  2925  	}, nil
  2926  }
  2927  
  2928  func (e *Engine) getTransferFundsFeesTransferRequest(ctx context.Context, t *types.Transfer, accountType types.AccountType) (*types.TransferRequest, error) {
  2929  	// only type supported here
  2930  	if t.Type != types.TransferTypeInfrastructureFeePay {
  2931  		return nil, errors.New("only infrastructure fee distribute type supported")
  2932  	}
  2933  
  2934  	var (
  2935  		fromAcc, infra *types.Account
  2936  		err            error
  2937  	)
  2938  
  2939  	switch accountType {
  2940  	case types.AccountTypeGeneral:
  2941  		fromAcc, err = e.GetPartyGeneralAccount(t.Owner, t.Amount.Asset)
  2942  		if err != nil {
  2943  			return nil, fmt.Errorf("account does not exists: %v, %v, %v",
  2944  				accountType, t.Owner, t.Amount.Asset,
  2945  			)
  2946  		}
  2947  	case types.AccountTypeLockedForStaking:
  2948  		fromAcc, err = e.GetPartyLockedForStaking(t.Owner, t.Amount.Asset)
  2949  		if err != nil {
  2950  			return nil, fmt.Errorf("account does not exists: %v, %v, %v",
  2951  				accountType, t.Owner, t.Amount.Asset,
  2952  			)
  2953  		}
  2954  	case types.AccountTypeVestedRewards:
  2955  		fromAcc = e.GetOrCreatePartyVestedRewardAccount(ctx, t.Owner, t.Amount.Asset)
  2956  
  2957  	default:
  2958  		return nil, fmt.Errorf("unsupported from account for TransferFunds: %v", accountType.String())
  2959  	}
  2960  
  2961  	infraID := e.accountID(noMarket, systemOwner, t.Amount.Asset, types.AccountTypeFeesInfrastructure)
  2962  	if infra, err = e.GetAccountByID(infraID); err != nil {
  2963  		// tha should never happened, if we got there, the
  2964  		// asset exists and the infra fee therefore MUST exists
  2965  		e.log.Panic("missing fee account",
  2966  			logging.String("asset", t.Amount.Asset),
  2967  			logging.String("id", infraID),
  2968  			logging.Error(err),
  2969  		)
  2970  	}
  2971  
  2972  	// now we got all relevant accounts, we can build our request
  2973  	return &types.TransferRequest{
  2974  		FromAccount: []*types.Account{fromAcc},
  2975  		ToAccount:   []*types.Account{infra},
  2976  		Amount:      t.Amount.Amount.Clone(),
  2977  		MinAmount:   t.Amount.Amount.Clone(),
  2978  		Asset:       t.Amount.Asset,
  2979  		Type:        t.Type,
  2980  		TransferID:  t.TransferID,
  2981  	}, nil
  2982  }
  2983  
  2984  // getTransferRequest builds the request, and sets the required accounts based on the type of the Transfer argument.
  2985  func (e *Engine) getTransferRequest(p *types.Transfer, settle, insurance *types.Account, mEvt *marginUpdate, useGeneralAccountForMarginSearch bool) (*types.TransferRequest, error) {
  2986  	if p == nil || p.Amount == nil {
  2987  		return nil, nil
  2988  	}
  2989  	var (
  2990  		asset = p.Amount.Asset
  2991  		err   error
  2992  		eacc  *types.Account
  2993  
  2994  		req = types.TransferRequest{
  2995  			Asset: asset, // TBC
  2996  			Type:  p.Type,
  2997  		}
  2998  	)
  2999  	if p.Owner != types.NetworkParty {
  3000  		if p.Type == types.TransferTypeMTMLoss ||
  3001  			p.Type == types.TransferTypePerpFundingLoss ||
  3002  			p.Type == types.TransferTypeWin ||
  3003  			p.Type == types.TransferTypeMarginLow {
  3004  			// we do not care about errors here as the bond account is not mandatory for the transfers
  3005  			// a partry would have a bond account only if it was also a market maker
  3006  			mEvt.bond, _ = e.GetAccountByID(e.accountID(settle.MarketID, p.Owner, asset, types.AccountTypeBond))
  3007  		}
  3008  		if settle != nil && mEvt.margin == nil {
  3009  			// the accounts for the party we need
  3010  			// the accounts for the trader we need
  3011  			mEvt.margin, err = e.GetAccountByID(e.accountID(settle.MarketID, p.Owner, asset, types.AccountTypeMargin))
  3012  			if err != nil {
  3013  				e.log.Error(
  3014  					"Failed to get the party margin account",
  3015  					logging.String("owner-id", p.Owner),
  3016  					logging.String("market-id", settle.MarketID),
  3017  					logging.Error(err),
  3018  				)
  3019  				return nil, err
  3020  			}
  3021  		}
  3022  
  3023  		// we'll need this account for all transfer types anyway (settlements, margin-risk updates)
  3024  		if mEvt.general == nil {
  3025  			mEvt.general, err = e.GetAccountByID(e.accountID(noMarket, p.Owner, asset, types.AccountTypeGeneral))
  3026  			if err != nil {
  3027  				e.log.Error(
  3028  					"Failed to get the party general account",
  3029  					logging.String("owner-id", p.Owner),
  3030  					logging.String("market-id", settle.MarketID),
  3031  					logging.Error(err),
  3032  				)
  3033  				return nil, err
  3034  			}
  3035  		}
  3036  	} else if mEvt.general == nil {
  3037  		// for the event, the insurance pool acts as the margin/general account
  3038  		mEvt.general = insurance
  3039  	}
  3040  	if p.Type == types.TransferTypeWithdraw || p.Type == types.TransferTypeDeposit {
  3041  		// external account:
  3042  		eacc, err = e.GetAccountByID(e.accountID(noMarket, systemOwner, asset, types.AccountTypeExternal))
  3043  		if err != nil {
  3044  			// if we get here it means we have an enabled asset but have not made all the accounts for it
  3045  			// so something has gone very awry
  3046  			e.log.Panic(
  3047  				"Failed to get the asset external account",
  3048  				logging.String("asset", asset),
  3049  				logging.Error(err),
  3050  			)
  3051  		}
  3052  	}
  3053  	switch p.Type {
  3054  	// final settle, or MTM settle, makes no difference, it's win/loss still
  3055  	case types.TransferTypeLoss, types.TransferTypeMTMLoss, types.TransferTypePerpFundingLoss:
  3056  		req.ToAccount = []*types.Account{
  3057  			settle,
  3058  		}
  3059  		req.Amount = p.Amount.Amount.Clone()
  3060  		req.MinAmount = num.UintZero() // default value, but keep it here explicitly
  3061  		// losses are collected first from the margin account, then the general account, and finally
  3062  		// taken out of the insurance pool. Network party will only have insurance pool available
  3063  		if mEvt.bond != nil {
  3064  			if useGeneralAccountForMarginSearch {
  3065  				// network party will never have a bond account, so we know what to do
  3066  				req.FromAccount = []*types.Account{
  3067  					mEvt.margin,
  3068  					mEvt.general,
  3069  					mEvt.bond,
  3070  					insurance,
  3071  				}
  3072  			} else {
  3073  				req.FromAccount = []*types.Account{
  3074  					mEvt.margin,
  3075  					mEvt.bond,
  3076  					insurance,
  3077  				}
  3078  			}
  3079  		} else if p.Owner == types.NetworkParty {
  3080  			req.FromAccount = []*types.Account{
  3081  				insurance,
  3082  			}
  3083  		} else {
  3084  			if useGeneralAccountForMarginSearch {
  3085  				// regular party, no bond account:
  3086  				req.FromAccount = []*types.Account{
  3087  					mEvt.margin,
  3088  					mEvt.general,
  3089  					insurance,
  3090  				}
  3091  			} else {
  3092  				req.FromAccount = []*types.Account{
  3093  					mEvt.margin,
  3094  					insurance,
  3095  				}
  3096  			}
  3097  		}
  3098  	case types.TransferTypeWin, types.TransferTypeMTMWin, types.TransferTypePerpFundingWin:
  3099  		req.Amount = p.Amount.Amount.Clone()
  3100  		req.MinAmount = num.UintZero() // default value, but keep it here explicitly
  3101  		// the insurance pool in the Req.FromAccountAccount is not used ATM (losses should fully cover wins
  3102  		// or the insurance pool has already been drained).
  3103  		if p.Owner == types.NetworkParty {
  3104  			req.FromAccount = []*types.Account{
  3105  				settle,
  3106  			}
  3107  			req.ToAccount = []*types.Account{
  3108  				insurance,
  3109  			}
  3110  		} else {
  3111  			req.FromAccount = []*types.Account{
  3112  				settle,
  3113  				insurance,
  3114  			}
  3115  			req.ToAccount = []*types.Account{
  3116  				mEvt.margin,
  3117  			}
  3118  		}
  3119  	case types.TransferTypeMarginLow:
  3120  		if mEvt.bond != nil {
  3121  			req.FromAccount = []*types.Account{
  3122  				mEvt.general,
  3123  				mEvt.bond,
  3124  			}
  3125  		} else {
  3126  			req.FromAccount = []*types.Account{
  3127  				mEvt.general,
  3128  			}
  3129  		}
  3130  		req.ToAccount = []*types.Account{
  3131  			mEvt.margin,
  3132  		}
  3133  		req.Amount = p.Amount.Amount.Clone()
  3134  		req.MinAmount = p.MinAmount.Clone()
  3135  	case types.TransferTypeMarginHigh:
  3136  		req.FromAccount = []*types.Account{
  3137  			mEvt.margin,
  3138  		}
  3139  		req.ToAccount = []*types.Account{
  3140  			mEvt.general,
  3141  		}
  3142  		req.Amount = p.Amount.Amount.Clone()
  3143  		req.MinAmount = p.MinAmount.Clone()
  3144  	case types.TransferTypeIsolatedMarginLow:
  3145  		mEvt.orderMargin, _ = e.GetAccountByID(e.accountID(settle.MarketID, p.Owner, asset, types.AccountTypeOrderMargin))
  3146  		req.FromAccount = []*types.Account{
  3147  			mEvt.orderMargin,
  3148  		}
  3149  		req.ToAccount = []*types.Account{
  3150  			mEvt.margin,
  3151  		}
  3152  		req.Amount = p.Amount.Amount.Clone()
  3153  		req.MinAmount = p.MinAmount.Clone()
  3154  	case types.TransferTypeOrderMarginLow:
  3155  		mEvt.orderMargin, _ = e.GetAccountByID(e.accountID(settle.MarketID, p.Owner, asset, types.AccountTypeOrderMargin))
  3156  		req.FromAccount = []*types.Account{
  3157  			mEvt.general,
  3158  		}
  3159  		req.ToAccount = []*types.Account{
  3160  			mEvt.orderMargin,
  3161  		}
  3162  		req.Amount = p.Amount.Amount.Clone()
  3163  		req.MinAmount = p.MinAmount.Clone()
  3164  	case types.TransferTypeOrderMarginHigh:
  3165  		mEvt.orderMargin, _ = e.GetAccountByID(e.accountID(settle.MarketID, p.Owner, asset, types.AccountTypeOrderMargin))
  3166  		req.FromAccount = []*types.Account{
  3167  			mEvt.orderMargin,
  3168  		}
  3169  		req.ToAccount = []*types.Account{
  3170  			mEvt.general,
  3171  		}
  3172  		req.Amount = p.Amount.Amount.Clone()
  3173  		req.MinAmount = p.MinAmount.Clone()
  3174  	case types.TransferTypeDeposit:
  3175  		// ensure we have the funds req.ToAccount deposit
  3176  		eacc.Balance = eacc.Balance.Add(eacc.Balance, p.Amount.Amount)
  3177  		req.FromAccount = []*types.Account{
  3178  			eacc,
  3179  		}
  3180  		// Look for the special case where we are topping up the reward account
  3181  		if p.Owner == rewardPartyID {
  3182  			rewardAcct, _ := e.GetGlobalRewardAccount(p.Amount.Asset)
  3183  			req.ToAccount = []*types.Account{
  3184  				rewardAcct,
  3185  			}
  3186  		} else {
  3187  			req.ToAccount = []*types.Account{
  3188  				mEvt.general,
  3189  			}
  3190  		}
  3191  		req.Amount = p.Amount.Amount.Clone()
  3192  		req.MinAmount = p.Amount.Amount.Clone()
  3193  	case types.TransferTypeWithdraw:
  3194  		req.FromAccount = []*types.Account{
  3195  			mEvt.general,
  3196  		}
  3197  		req.ToAccount = []*types.Account{
  3198  			eacc,
  3199  		}
  3200  		req.Amount = p.Amount.Amount.Clone()
  3201  		req.MinAmount = p.Amount.Amount.Clone()
  3202  	case types.TransferTypeRewardPayout:
  3203  		rewardAcct, err := e.GetGlobalRewardAccount(asset)
  3204  		if err != nil {
  3205  			return nil, errors.New("unable to get the global reward account")
  3206  		}
  3207  		req.FromAccount = []*types.Account{
  3208  			rewardAcct,
  3209  		}
  3210  		req.ToAccount = []*types.Account{
  3211  			mEvt.general,
  3212  		}
  3213  		req.Amount = p.Amount.Amount.Clone()
  3214  		req.MinAmount = p.Amount.Amount.Clone()
  3215  	default:
  3216  		return nil, errors.New("unexpected transfer type")
  3217  	}
  3218  	return &req, nil
  3219  }
  3220  
  3221  // this builds a TransferResponse for a specific request, we collect all of them and aggregate.
  3222  func (e *Engine) getLedgerEntries(ctx context.Context, req *types.TransferRequest) (ret *types.LedgerMovement, err error) {
  3223  	ret = &types.LedgerMovement{
  3224  		Entries:  []*types.LedgerEntry{},
  3225  		Balances: make([]*types.PostTransferBalance, 0, len(req.ToAccount)),
  3226  	}
  3227  	for _, t := range req.ToAccount {
  3228  		ret.Balances = append(ret.Balances, &types.PostTransferBalance{
  3229  			Account: t,
  3230  			Balance: num.UintZero(),
  3231  		})
  3232  	}
  3233  	amount := req.Amount
  3234  	now := e.timeService.GetTimeNow().UnixNano()
  3235  	for _, acc := range req.FromAccount {
  3236  		// give each to account an equal share
  3237  		nToAccounts := num.NewUint(uint64(len(req.ToAccount)))
  3238  		parts := num.UintZero().Div(amount, nToAccounts)
  3239  		// add remaining pennies to last ledger movement
  3240  		remainder := num.UintZero().Mod(amount, nToAccounts)
  3241  		var (
  3242  			to *types.PostTransferBalance
  3243  			lm *types.LedgerEntry
  3244  		)
  3245  		// either the account contains enough, or we're having to access insurance pool money
  3246  		if acc.Balance.GTE(amount) {
  3247  			acc.Balance.Sub(acc.Balance, amount)
  3248  			if err := e.UpdateBalance(ctx, acc.ID, acc.Balance); err != nil {
  3249  				e.log.Error(
  3250  					"Failed to update balance for account",
  3251  					logging.String("account-id", acc.ID),
  3252  					logging.BigUint("balance", acc.Balance),
  3253  					logging.Error(err),
  3254  				)
  3255  				return nil, err
  3256  			}
  3257  			for _, to = range ret.Balances {
  3258  				lm = &types.LedgerEntry{
  3259  					FromAccount:        acc.ToDetails(),
  3260  					ToAccount:          to.Account.ToDetails(),
  3261  					Amount:             parts.Clone(),
  3262  					Type:               req.Type,
  3263  					Timestamp:          now,
  3264  					FromAccountBalance: acc.Balance.Clone(),
  3265  					ToAccountBalance:   num.Sum(to.Account.Balance, parts),
  3266  					TransferID:         req.TransferID,
  3267  				}
  3268  				ret.Entries = append(ret.Entries, lm)
  3269  				to.Balance.AddSum(parts)
  3270  				to.Account.Balance.AddSum(parts)
  3271  			}
  3272  			// add remainder
  3273  			if !remainder.IsZero() && lm != nil {
  3274  				lm.Amount.AddSum(remainder)
  3275  				to.Balance.AddSum(remainder)
  3276  				to.Account.Balance.AddSum(remainder)
  3277  			}
  3278  			return ret, nil
  3279  		}
  3280  		if !acc.Balance.IsZero() {
  3281  			amount.Sub(amount, acc.Balance)
  3282  			// partial amount resolves differently
  3283  			parts.Div(acc.Balance, nToAccounts)
  3284  			acc.Balance.SetUint64(0)
  3285  			if err := e.UpdateBalance(ctx, acc.ID, acc.Balance); err != nil {
  3286  				e.log.Error(
  3287  					"Failed to set balance of account to 0",
  3288  					logging.String("account-id", acc.ID),
  3289  					logging.Error(err),
  3290  				)
  3291  				return nil, err
  3292  			}
  3293  			for _, to = range ret.Balances {
  3294  				ret.Entries = append(ret.Entries, &types.LedgerEntry{
  3295  					FromAccount:        acc.ToDetails(),
  3296  					ToAccount:          to.Account.ToDetails(),
  3297  					Amount:             parts,
  3298  					Type:               req.Type,
  3299  					Timestamp:          now,
  3300  					FromAccountBalance: acc.Balance.Clone(),
  3301  					ToAccountBalance:   num.Sum(to.Account.Balance, parts),
  3302  				})
  3303  				to.Account.Balance.AddSum(parts)
  3304  				to.Balance.AddSum(parts)
  3305  			}
  3306  		}
  3307  		if amount.IsZero() {
  3308  			break
  3309  		}
  3310  	}
  3311  	return ret, nil
  3312  }
  3313  
  3314  func (e *Engine) clearAccount(
  3315  	ctx context.Context, req *types.TransferRequest,
  3316  	party, asset, market string,
  3317  ) (*types.LedgerMovement, error) {
  3318  	ledgerEntries, err := e.getLedgerEntries(ctx, req)
  3319  	if err != nil {
  3320  		e.log.Error(
  3321  			"Failed to move monies from margin to general account",
  3322  			logging.PartyID(party),
  3323  			logging.MarketID(market),
  3324  			logging.AssetID(asset),
  3325  			logging.Error(err))
  3326  		return nil, err
  3327  	}
  3328  
  3329  	for _, v := range ledgerEntries.Entries {
  3330  		// increment the to account
  3331  		if err := e.IncrementBalance(ctx, e.ADtoID(v.ToAccount), v.Amount); err != nil {
  3332  			e.log.Error(
  3333  				"Failed to increment balance for account",
  3334  				logging.String("asset", v.ToAccount.AssetID),
  3335  				logging.String("market", v.ToAccount.MarketID),
  3336  				logging.String("owner", v.ToAccount.Owner),
  3337  				logging.String("type", v.ToAccount.Type.String()),
  3338  				logging.BigUint("amount", v.Amount),
  3339  				logging.Error(err),
  3340  			)
  3341  			return nil, err
  3342  		}
  3343  	}
  3344  
  3345  	// we remove the margin account
  3346  	e.removeAccount(req.FromAccount[0].ID)
  3347  	// remove account from balances tracking
  3348  	e.rmPartyAccount(party, req.FromAccount[0].ID)
  3349  
  3350  	return ledgerEntries, nil
  3351  }
  3352  
  3353  // ClearMarket will remove all monies or accounts for parties allocated for a market (margin accounts)
  3354  // when the market reach end of life (maturity).
  3355  func (e *Engine) ClearMarket(ctx context.Context, mktID, asset string, parties []string, keepInsurance bool) ([]*types.LedgerMovement, error) {
  3356  	// create a transfer request that we will reuse all the time in order to make allocations smaller
  3357  	req := &types.TransferRequest{
  3358  		FromAccount: make([]*types.Account, 1),
  3359  		ToAccount:   make([]*types.Account, 1),
  3360  		Asset:       asset,
  3361  		Type:        types.TransferTypeClearAccount,
  3362  	}
  3363  
  3364  	// assume we have as much transfer response than parties
  3365  	resps := make([]*types.LedgerMovement, 0, len(parties))
  3366  
  3367  	for _, v := range parties {
  3368  		generalAcc, err := e.GetAccountByID(e.accountID(noMarket, v, asset, types.AccountTypeGeneral))
  3369  		if err != nil {
  3370  			e.log.Debug(
  3371  				"Failed to get the general account",
  3372  				logging.String("party-id", v),
  3373  				logging.String("market-id", mktID),
  3374  				logging.String("asset", asset),
  3375  				logging.Error(err))
  3376  			// just try to do other parties
  3377  			continue
  3378  		}
  3379  
  3380  		// we start first with the margin account if it exists
  3381  		marginAcc, err := e.GetAccountByID(e.accountID(mktID, v, asset, types.AccountTypeMargin))
  3382  		if err != nil {
  3383  			e.log.Debug(
  3384  				"Failed to get the margin account",
  3385  				logging.String("party-id", v),
  3386  				logging.String("market-id", mktID),
  3387  				logging.String("asset", asset),
  3388  				logging.Error(err))
  3389  		} else {
  3390  			req.FromAccount[0] = marginAcc
  3391  			req.ToAccount[0] = generalAcc
  3392  			req.Amount = marginAcc.Balance
  3393  
  3394  			if e.log.GetLevel() == logging.DebugLevel {
  3395  				e.log.Debug("Clearing party margin account",
  3396  					logging.String("market-id", mktID),
  3397  					logging.String("asset", asset),
  3398  					logging.String("party", v),
  3399  					logging.BigUint("margin-before", marginAcc.Balance),
  3400  					logging.BigUint("general-before", generalAcc.Balance),
  3401  					logging.BigUint("general-after", num.Sum(generalAcc.Balance, marginAcc.Balance)))
  3402  			}
  3403  
  3404  			ledgerEntries, err := e.clearAccount(ctx, req, v, asset, mktID)
  3405  			if err != nil {
  3406  				e.log.Panic("unable to clear party account", logging.Error(err))
  3407  			}
  3408  
  3409  			// as the entries to the response
  3410  			resps = append(resps, ledgerEntries)
  3411  		}
  3412  		// clear order margin account
  3413  		orderMarginAcc, err := e.GetAccountByID(e.accountID(mktID, v, asset, types.AccountTypeOrderMargin))
  3414  		if err != nil {
  3415  			e.log.Debug(
  3416  				"Failed to get the order margin account",
  3417  				logging.String("party-id", v),
  3418  				logging.String("market-id", mktID),
  3419  				logging.String("asset", asset),
  3420  				logging.Error(err))
  3421  		} else {
  3422  			req.FromAccount[0] = orderMarginAcc
  3423  			req.ToAccount[0] = generalAcc
  3424  			req.Amount = orderMarginAcc.Balance
  3425  
  3426  			if e.log.GetLevel() == logging.DebugLevel {
  3427  				e.log.Debug("Clearing party order margin account",
  3428  					logging.String("market-id", mktID),
  3429  					logging.String("asset", asset),
  3430  					logging.String("party", v),
  3431  					logging.BigUint("margin-before", orderMarginAcc.Balance),
  3432  					logging.BigUint("general-before", generalAcc.Balance),
  3433  					logging.BigUint("general-after", num.Sum(generalAcc.Balance, orderMarginAcc.Balance)))
  3434  			}
  3435  
  3436  			ledgerEntries, err := e.clearAccount(ctx, req, v, asset, mktID)
  3437  			if err != nil {
  3438  				e.log.Panic("unable to clear party account", logging.Error(err))
  3439  			}
  3440  
  3441  			// as the entries to the response
  3442  			resps = append(resps, ledgerEntries)
  3443  		}
  3444  
  3445  		// Then we do bond account
  3446  		bondAcc, err := e.GetAccountByID(e.accountID(mktID, v, asset, types.AccountTypeBond))
  3447  		if err != nil {
  3448  			// this not an actual error
  3449  			// a party may not have a bond account if
  3450  			// its not also a liquidity provider
  3451  			continue
  3452  		}
  3453  
  3454  		req.FromAccount[0] = bondAcc
  3455  		req.ToAccount[0] = generalAcc
  3456  		req.Amount = bondAcc.Balance.Clone()
  3457  
  3458  		if e.log.GetLevel() == logging.DebugLevel {
  3459  			e.log.Debug("Clearing party bond account",
  3460  				logging.String("market-id", mktID),
  3461  				logging.String("asset", asset),
  3462  				logging.String("party", v),
  3463  				logging.BigUint("bond-before", bondAcc.Balance),
  3464  				logging.BigUint("general-before", generalAcc.Balance),
  3465  				logging.BigUint("general-after", num.Sum(generalAcc.Balance, marginAcc.Balance)))
  3466  		}
  3467  
  3468  		ledgerEntries, err := e.clearAccount(ctx, req, v, asset, mktID)
  3469  		if err != nil {
  3470  			e.log.Panic("unable to clear party account", logging.Error(err))
  3471  		}
  3472  
  3473  		// add entries to the response
  3474  		resps = append(resps, ledgerEntries)
  3475  	}
  3476  	if lpFeeLE := e.clearRemainingLPFees(ctx, mktID, asset, keepInsurance); len(lpFeeLE) > 0 {
  3477  		resps = append(resps, lpFeeLE...)
  3478  	}
  3479  
  3480  	if keepInsurance {
  3481  		return resps, nil
  3482  	}
  3483  	insLM, err := e.ClearInsurancepool(ctx, mktID, asset, false)
  3484  	if err != nil {
  3485  		return nil, err
  3486  	}
  3487  	if insLM == nil {
  3488  		return resps, nil
  3489  	}
  3490  	return append(resps, insLM...), nil
  3491  }
  3492  
  3493  func (e *Engine) clearRemainingLPFees(ctx context.Context, mktID, asset string, keepFeeAcc bool) []*types.LedgerMovement {
  3494  	// we need a market insurance pool regardless
  3495  	marketInsuranceAcc := e.GetOrCreateMarketInsurancePoolAccount(ctx, mktID, asset)
  3496  	// any remaining balance in the fee account gets transferred over to the insurance account
  3497  	lpFeeAccID := e.accountID(mktID, "", asset, types.AccountTypeFeesLiquidity)
  3498  	ret := make([]*types.LedgerMovement, 0, 4)
  3499  	req := &types.TransferRequest{
  3500  		FromAccount: make([]*types.Account, 1),
  3501  		ToAccount:   []*types.Account{marketInsuranceAcc},
  3502  		Asset:       asset,
  3503  		Type:        types.TransferTypeClearAccount,
  3504  	}
  3505  
  3506  	lpFeeAcc, exists := e.accs[lpFeeAccID]
  3507  	if exists && !lpFeeAcc.Balance.IsZero() {
  3508  		req.FromAccount[0] = lpFeeAcc
  3509  		req.Amount = lpFeeAcc.Balance.Clone()
  3510  		lpFeeLE, err := e.getLedgerEntries(ctx, req)
  3511  		if err != nil {
  3512  			e.log.Panic("unable to redistribute remainder of LP fee account funds", logging.Error(err))
  3513  		}
  3514  
  3515  		for _, bal := range lpFeeLE.Balances {
  3516  			if err := e.IncrementBalance(ctx, bal.Account.ID, bal.Balance); err != nil {
  3517  				e.log.Error("Could not update the target account in transfer",
  3518  					logging.String("account-id", bal.Account.ID),
  3519  					logging.Error(err))
  3520  				return nil
  3521  			}
  3522  		}
  3523  		ret = append(ret, lpFeeLE)
  3524  	}
  3525  
  3526  	// only remove this account when the market is ready to be fully cleared
  3527  	if exists && !keepFeeAcc {
  3528  		e.removeAccount(lpFeeAccID)
  3529  	}
  3530  
  3531  	// clear remaining maker fees
  3532  	makerAcc, err := e.GetMarketMakerFeeAccount(mktID, asset)
  3533  	if err == nil && makerAcc != nil && !makerAcc.Balance.IsZero() {
  3534  		req.FromAccount[0] = makerAcc
  3535  		req.Amount = makerAcc.Balance.Clone()
  3536  		mLE, err := e.getLedgerEntries(ctx, req)
  3537  		if err != nil {
  3538  			e.log.Panic("unable to redistribute remainder of maker fee account funds", logging.Error(err))
  3539  		}
  3540  		if !keepFeeAcc {
  3541  			e.removeAccount(makerAcc.ID)
  3542  		}
  3543  		for _, bal := range mLE.Balances {
  3544  			if err := e.IncrementBalance(ctx, bal.Account.ID, bal.Balance); err != nil {
  3545  				e.log.Error("Could not update the target account in transfer",
  3546  					logging.String("account-id", bal.Account.ID),
  3547  					logging.Error(err))
  3548  				return nil
  3549  			}
  3550  		}
  3551  		ret = append(ret, mLE)
  3552  	}
  3553  	// clear settlement balance (if any)
  3554  	settle, _, _ := e.getSystemAccounts(mktID, asset)
  3555  	if settle == nil || settle.Balance.IsZero() {
  3556  		return ret
  3557  	}
  3558  	req.FromAccount[0] = settle
  3559  	req.Amount = settle.Balance.Clone()
  3560  	scLE, err := e.getLedgerEntries(ctx, req)
  3561  	if err != nil {
  3562  		e.log.Panic("unable to clear remaining settlement balance", logging.Error(err))
  3563  	}
  3564  	for _, bal := range scLE.Balances {
  3565  		if err := e.IncrementBalance(ctx, bal.Account.ID, bal.Balance); err != nil {
  3566  			e.log.Error("Could not update the target account in transfer",
  3567  				logging.String("account-id", bal.Account.ID),
  3568  				logging.Error(err))
  3569  			return nil
  3570  		}
  3571  	}
  3572  	return append(ret, scLE)
  3573  }
  3574  
  3575  func (e *Engine) ClearInsurancepool(ctx context.Context, mktID, asset string, clearFees bool) ([]*types.LedgerMovement, error) {
  3576  	resp := make([]*types.LedgerMovement, 0, 3)
  3577  	if clearFees {
  3578  		if r := e.clearRemainingLPFees(ctx, mktID, asset, false); len(r) > 0 {
  3579  			resp = append(resp, r...)
  3580  		}
  3581  	}
  3582  	req := &types.TransferRequest{
  3583  		FromAccount: make([]*types.Account, 1),
  3584  		ToAccount:   make([]*types.Account, 1),
  3585  		Asset:       asset,
  3586  		Type:        types.TransferTypeClearAccount,
  3587  	}
  3588  	marketInsuranceAcc := e.GetOrCreateMarketInsurancePoolAccount(ctx, mktID, asset)
  3589  	marketInsuranceID := marketInsuranceAcc.ID
  3590  	// redistribute the remaining funds in the market insurance account between other markets insurance accounts and global insurance account
  3591  	if marketInsuranceAcc.Balance.IsZero() {
  3592  		// if there's no market insurance account or it has no balance, nothing to do here
  3593  		e.removeAccount(marketInsuranceID)
  3594  		return nil, nil
  3595  	}
  3596  
  3597  	globalIns, _ := e.GetGlobalInsuranceAccount(asset)
  3598  	// redistribute market insurance funds between the global and other markets equally
  3599  	req.FromAccount[0] = marketInsuranceAcc
  3600  	req.ToAccount[0] = globalIns
  3601  	req.Amount = marketInsuranceAcc.Balance.Clone()
  3602  	insuranceLedgerEntries, err := e.getLedgerEntries(ctx, req)
  3603  	if err != nil {
  3604  		e.log.Panic("unable to redistribute market insurance funds", logging.Error(err))
  3605  	}
  3606  	for _, bal := range insuranceLedgerEntries.Balances {
  3607  		if err := e.IncrementBalance(ctx, bal.Account.ID, bal.Balance); err != nil {
  3608  			e.log.Error("Could not update the target account in transfer",
  3609  				logging.String("account-id", bal.Account.ID),
  3610  				logging.Error(err))
  3611  			return nil, err
  3612  		}
  3613  	}
  3614  	resp = append(resp, insuranceLedgerEntries)
  3615  	e.removeAccount(marketInsuranceID)
  3616  	return resp, nil
  3617  }
  3618  
  3619  func (e *Engine) CanCoverBond(market, party, asset string, amount *num.Uint) bool {
  3620  	bondID := e.accountID(
  3621  		market, party, asset, types.AccountTypeBond,
  3622  	)
  3623  	genID := e.accountID(
  3624  		noMarket, party, asset, types.AccountTypeGeneral,
  3625  	)
  3626  
  3627  	availableBalance := num.UintZero()
  3628  
  3629  	bondAcc, ok := e.accs[bondID]
  3630  	if ok {
  3631  		availableBalance.AddSum(bondAcc.Balance)
  3632  	}
  3633  	genAcc, ok := e.accs[genID]
  3634  	if ok {
  3635  		availableBalance.AddSum(genAcc.Balance)
  3636  	}
  3637  
  3638  	return availableBalance.GTE(amount)
  3639  }
  3640  
  3641  // GetOrCreatePartyBondAccount returns a bond account given a set of parameters.
  3642  // crates it if not exists.
  3643  func (e *Engine) GetOrCreatePartyBondAccount(ctx context.Context, partyID, marketID, asset string) (*types.Account, error) {
  3644  	if !e.AssetExists(asset) {
  3645  		return nil, ErrInvalidAssetID
  3646  	}
  3647  
  3648  	bondID, err := e.CreatePartyBondAccount(ctx, partyID, marketID, asset)
  3649  	if err != nil {
  3650  		return nil, err
  3651  	}
  3652  	return e.GetAccountByID(bondID)
  3653  }
  3654  
  3655  // CreatePartyBondAccount creates a bond account if it does not exist, will return an error
  3656  // if no general account exist for the party for the given asset.
  3657  func (e *Engine) CreatePartyBondAccount(ctx context.Context, partyID, marketID, asset string) (string, error) {
  3658  	if !e.AssetExists(asset) {
  3659  		return "", ErrInvalidAssetID
  3660  	}
  3661  	bondID := e.accountID(marketID, partyID, asset, types.AccountTypeBond)
  3662  	if _, ok := e.accs[bondID]; !ok {
  3663  		// OK no bond ID, so let's try to get the general id then
  3664  		// first check if general account exists
  3665  		generalID := e.accountID(noMarket, partyID, asset, types.AccountTypeGeneral)
  3666  		if _, ok := e.accs[generalID]; !ok {
  3667  			e.log.Error("Tried to create a bond account for a party with no general account",
  3668  				logging.String("party-id", partyID),
  3669  				logging.String("asset", asset),
  3670  				logging.String("market-id", marketID),
  3671  			)
  3672  			return "", ErrNoGeneralAccountWhenCreateBondAccount
  3673  		}
  3674  
  3675  		// general account id OK, let's create a margin account
  3676  		acc := types.Account{
  3677  			ID:       bondID,
  3678  			Asset:    asset,
  3679  			MarketID: marketID,
  3680  			Balance:  num.UintZero(),
  3681  			Owner:    partyID,
  3682  			Type:     types.AccountTypeBond,
  3683  		}
  3684  		e.accs[bondID] = &acc
  3685  		e.addPartyAccount(partyID, bondID, &acc)
  3686  		e.addAccountToHashableSlice(&acc)
  3687  		e.broker.Send(events.NewAccountEvent(ctx, acc))
  3688  	}
  3689  	return bondID, nil
  3690  }
  3691  
  3692  // GetOrCreatePartyLiquidityFeeAccount returns a party liquidity fee account given a set of parameters.
  3693  // Crates it if not exists.
  3694  func (e *Engine) GetOrCreatePartyLiquidityFeeAccount(ctx context.Context, partyID, marketID, asset string) (*types.Account, error) {
  3695  	if !e.AssetExists(asset) {
  3696  		return nil, ErrInvalidAssetID
  3697  	}
  3698  
  3699  	accID, err := e.CreatePartyLiquidityFeeAccount(ctx, partyID, marketID, asset)
  3700  	if err != nil {
  3701  		return nil, err
  3702  	}
  3703  	return e.GetAccountByID(accID)
  3704  }
  3705  
  3706  // CreatePartyLiquidityFeeAccount creates a bond account if it does not exist, will return an error
  3707  // if no general account exist for the party for the given asset.
  3708  func (e *Engine) CreatePartyLiquidityFeeAccount(ctx context.Context, partyID, marketID, asset string) (string, error) {
  3709  	if !e.AssetExists(asset) {
  3710  		return "", ErrInvalidAssetID
  3711  	}
  3712  	lpFeeAccountID := e.accountID(marketID, partyID, asset, types.AccountTypeLPLiquidityFees)
  3713  	if _, ok := e.accs[lpFeeAccountID]; !ok {
  3714  		// OK no bond ID, so let's try to get the general id then.
  3715  		// First check if general account exists.
  3716  		generalID := e.accountID(noMarket, partyID, asset, types.AccountTypeGeneral)
  3717  		if _, ok := e.accs[generalID]; !ok {
  3718  			e.log.Error("Tried to create a liquidity provision account for a party with no general account",
  3719  				logging.String("party-id", partyID),
  3720  				logging.String("asset", asset),
  3721  				logging.String("market-id", marketID),
  3722  			)
  3723  			return "", ErrNoGeneralAccountWhenCreateBondAccount
  3724  		}
  3725  
  3726  		// General account id OK, let's create a margin account.
  3727  		acc := types.Account{
  3728  			ID:       lpFeeAccountID,
  3729  			Asset:    asset,
  3730  			MarketID: marketID,
  3731  			Balance:  num.UintZero(),
  3732  			Owner:    partyID,
  3733  			Type:     types.AccountTypeLPLiquidityFees,
  3734  		}
  3735  		e.accs[lpFeeAccountID] = &acc
  3736  		e.addPartyAccount(partyID, lpFeeAccountID, &acc)
  3737  		e.addAccountToHashableSlice(&acc)
  3738  		e.broker.Send(events.NewAccountEvent(ctx, acc))
  3739  	}
  3740  	return lpFeeAccountID, nil
  3741  }
  3742  
  3743  // GetOrCreateLiquidityFeesBonusDistributionAccount returns a liquidity fees bonus distribution account given a set of parameters.
  3744  // crates it if not exists.
  3745  func (e *Engine) GetOrCreateLiquidityFeesBonusDistributionAccount(
  3746  	ctx context.Context,
  3747  	marketID,
  3748  	asset string,
  3749  ) (*types.Account, error) {
  3750  	if !e.AssetExists(asset) {
  3751  		return nil, ErrInvalidAssetID
  3752  	}
  3753  
  3754  	id := e.accountID(marketID, systemOwner, asset, types.AccountTypeLiquidityFeesBonusDistribution)
  3755  	acc, err := e.GetAccountByID(id)
  3756  	if err != nil {
  3757  		acc = &types.Account{
  3758  			ID:       id,
  3759  			Asset:    asset,
  3760  			Owner:    systemOwner,
  3761  			Balance:  num.UintZero(),
  3762  			MarketID: marketID,
  3763  			Type:     types.AccountTypeLiquidityFeesBonusDistribution,
  3764  		}
  3765  		e.accs[id] = acc
  3766  		e.addAccountToHashableSlice(acc)
  3767  		e.broker.Send(events.NewAccountEvent(ctx, *acc))
  3768  	}
  3769  	return acc, nil
  3770  }
  3771  
  3772  func (e *Engine) GetLiquidityFeesBonusDistributionAccount(marketID, asset string) (*types.Account, error) {
  3773  	id := e.accountID(marketID, systemOwner, asset, types.AccountTypeLiquidityFeesBonusDistribution)
  3774  	return e.GetAccountByID(id)
  3775  }
  3776  
  3777  func (e *Engine) RemoveLiquidityFeesBonusDistributionAccount(partyID, marketID, asset string) error {
  3778  	id := e.accountID(marketID, systemOwner, asset, types.AccountTypeLiquidityFeesBonusDistribution)
  3779  	acc, ok := e.accs[id]
  3780  	if !ok {
  3781  		return ErrAccountDoesNotExist
  3782  	}
  3783  	if !acc.Balance.IsZero() {
  3784  		e.log.Panic("attempting to delete a bond account with non-zero balance")
  3785  	}
  3786  	e.removeAccount(id)
  3787  	return nil
  3788  }
  3789  
  3790  // CreatePartyMarginAccount creates a margin account if it does not exist, will return an error
  3791  // if no general account exist for the party for the given asset.
  3792  func (e *Engine) CreatePartyMarginAccount(ctx context.Context, partyID, marketID, asset string) (string, error) {
  3793  	if !e.AssetExists(asset) {
  3794  		return "", ErrInvalidAssetID
  3795  	}
  3796  	marginID := e.accountID(marketID, partyID, asset, types.AccountTypeMargin)
  3797  	if _, ok := e.accs[marginID]; !ok {
  3798  		// OK no margin ID, so let's try to get the general id then
  3799  		// first check if general account exists
  3800  		generalID := e.accountID(noMarket, partyID, asset, types.AccountTypeGeneral)
  3801  		if _, ok := e.accs[generalID]; !ok {
  3802  			e.log.Error("Tried to create a margin account for a party with no general account",
  3803  				logging.String("party-id", partyID),
  3804  				logging.String("asset", asset),
  3805  				logging.String("market-id", marketID),
  3806  			)
  3807  			return "", ErrNoGeneralAccountWhenCreateMarginAccount
  3808  		}
  3809  
  3810  		// general account id OK, let's create a margin account
  3811  		acc := types.Account{
  3812  			ID:       marginID,
  3813  			Asset:    asset,
  3814  			MarketID: marketID,
  3815  			Balance:  num.UintZero(),
  3816  			Owner:    partyID,
  3817  			Type:     types.AccountTypeMargin,
  3818  		}
  3819  		e.accs[marginID] = &acc
  3820  		e.addPartyAccount(partyID, marginID, &acc)
  3821  		e.addAccountToHashableSlice(&acc)
  3822  		e.broker.Send(events.NewAccountEvent(ctx, acc))
  3823  	}
  3824  	return marginID, nil
  3825  }
  3826  
  3827  // CreatePartyAMMSubAccounts ...
  3828  func (e *Engine) CreatePartyAMMsSubAccounts(
  3829  	ctx context.Context,
  3830  	party, ammKey, asset, market string,
  3831  ) (general *types.Account, margin *types.Account, err error) {
  3832  	generalID, err := e.CreatePartyGeneralAccount(ctx, ammKey, asset)
  3833  	if err != nil {
  3834  		return nil, nil, err
  3835  	}
  3836  
  3837  	marginID, err := e.CreatePartyMarginAccount(ctx, ammKey, market, asset)
  3838  	if err != nil {
  3839  		return nil, nil, err
  3840  	}
  3841  
  3842  	_, err = e.CreatePartyLiquidityFeeAccount(ctx, ammKey, market, asset)
  3843  	if err != nil {
  3844  		return nil, nil, err
  3845  	}
  3846  
  3847  	return e.accs[generalID].Clone(), e.accs[marginID].Clone(), nil
  3848  }
  3849  
  3850  func (e *Engine) getSubAccounts(subAccount, owner, asset, market string) (*types.Account, *types.Account, *types.Account, error) {
  3851  	ownerGeneral, err := e.GetAccountByID(e.accountID(noMarket, owner, asset, types.AccountTypeGeneral))
  3852  	if err != nil {
  3853  		e.log.Error(
  3854  			"Failed to get the party general account",
  3855  			logging.String("owner-id", owner),
  3856  			logging.String("market-id", market),
  3857  			logging.Error(err),
  3858  		)
  3859  		return nil, nil, nil, err
  3860  	}
  3861  
  3862  	// we'll need this account for all transfer types anyway (settlements, margin-risk updates)
  3863  	subAccountGeneral, err := e.GetAccountByID(e.accountID(noMarket, subAccount, asset, types.AccountTypeGeneral))
  3864  	if err != nil {
  3865  		e.log.Error(
  3866  			"Failed to get the party sub account",
  3867  			logging.String("owner-id", owner),
  3868  			logging.String("market-id", market),
  3869  			logging.Error(err),
  3870  		)
  3871  		return nil, nil, nil, err
  3872  	}
  3873  
  3874  	// we'll need this account for all transfer types anyway (settlements, margin-risk updates)
  3875  	subAccountMargin, err := e.GetAccountByID(e.accountID(market, subAccount, asset, types.AccountTypeMargin))
  3876  	if err != nil {
  3877  		e.log.Error(
  3878  			"Failed to get the party margin sub account",
  3879  			logging.String("owner-id", owner),
  3880  			logging.String("market-id", market),
  3881  			logging.Error(err),
  3882  		)
  3883  		return nil, nil, nil, err
  3884  	}
  3885  
  3886  	return ownerGeneral, subAccountGeneral, subAccountMargin, nil
  3887  }
  3888  
  3889  func (e *Engine) getSubAccountTransferRequest(
  3890  	party, subAccount, asset, market string,
  3891  	amount *num.Uint,
  3892  	typ types.TransferType,
  3893  ) (*types.TransferRequest, error) {
  3894  	ownerGeneral, subAccountGeneral, subAccountMargin, err := e.getSubAccounts(subAccount, party, asset, market)
  3895  	if err != nil {
  3896  		return nil, err
  3897  	}
  3898  
  3899  	treq := &types.TransferRequest{
  3900  		Amount:    amount.Clone(),
  3901  		MinAmount: amount.Clone(),
  3902  		Asset:     asset,
  3903  		Type:      typ,
  3904  	}
  3905  
  3906  	switch typ {
  3907  	case types.TransferTypeAMMLow:
  3908  		// do we have enough in the general account to make the transfer?
  3909  		if !amount.IsZero() && ownerGeneral.Balance.LT(amount) {
  3910  			return nil, errors.New("not enough collateral in general account")
  3911  		}
  3912  		treq.FromAccount = []*types.Account{ownerGeneral}
  3913  		treq.ToAccount = []*types.Account{subAccountGeneral}
  3914  		return treq, nil
  3915  	case types.TransferTypeAMMHigh:
  3916  		treq.FromAccount = []*types.Account{subAccountGeneral, subAccountMargin}
  3917  		treq.ToAccount = []*types.Account{ownerGeneral}
  3918  		return treq, nil
  3919  	default:
  3920  		return nil, errors.New("unsupported transfer type for sub accounts")
  3921  	}
  3922  }
  3923  
  3924  func (e *Engine) SubAccountUpdate(
  3925  	ctx context.Context,
  3926  	party, subAccount, asset, market string,
  3927  	transferType types.TransferType,
  3928  	amount *num.Uint,
  3929  ) (*types.LedgerMovement, error) {
  3930  	req, err := e.getSubAccountTransferRequest(party, subAccount, asset, market, amount, transferType)
  3931  	if err != nil {
  3932  		return nil, err
  3933  	}
  3934  
  3935  	res, err := e.getLedgerEntries(ctx, req)
  3936  	if err != nil {
  3937  		return nil, err
  3938  	}
  3939  
  3940  	for _, v := range res.Entries {
  3941  		// increment the to account
  3942  		if err := e.IncrementBalance(ctx, e.ADtoID(v.ToAccount), v.Amount); err != nil {
  3943  			e.log.Error(
  3944  				"Failed to increment balance for account",
  3945  				logging.String("asset", v.ToAccount.AssetID),
  3946  				logging.String("market", v.ToAccount.MarketID),
  3947  				logging.String("owner", v.ToAccount.Owner),
  3948  				logging.String("type", v.ToAccount.Type.String()),
  3949  				logging.BigUint("amount", v.Amount),
  3950  				logging.Error(err),
  3951  			)
  3952  		}
  3953  	}
  3954  
  3955  	return res, nil
  3956  }
  3957  
  3958  func (e *Engine) SubAccountClosed(ctx context.Context, party, subAccount, asset, market string) ([]*types.LedgerMovement, error) {
  3959  	lMovements := make([]*types.LedgerMovement, 0, 2)
  3960  	// NOTE: use subAccount as party ID here to release the margin sub-account to the general sub-account
  3961  	mlm, err := e.ClearPartyMarginAccount(ctx, subAccount, market, asset)
  3962  	if err != nil {
  3963  		return nil, err
  3964  	}
  3965  	if mlm != nil {
  3966  		lMovements = append(lMovements, mlm)
  3967  	}
  3968  	ownerGeneral, subAccountGeneral, _, err := e.getSubAccounts(subAccount, party, asset, market)
  3969  	if err != nil {
  3970  		return nil, err
  3971  	}
  3972  	// one transfer from general to general
  3973  	treq := &types.TransferRequest{
  3974  		Amount:      subAccountGeneral.Balance.Clone(),
  3975  		MinAmount:   subAccountGeneral.Balance.Clone(),
  3976  		Asset:       asset,
  3977  		Type:        types.TransferTypeAMMRelease,
  3978  		FromAccount: []*types.Account{subAccountGeneral},
  3979  		ToAccount:   []*types.Account{ownerGeneral},
  3980  	}
  3981  
  3982  	gres, err := e.getLedgerEntries(ctx, treq)
  3983  	if err != nil {
  3984  		return nil, err
  3985  	}
  3986  
  3987  	for _, v := range gres.Entries {
  3988  		// increment the to account
  3989  		if err := e.IncrementBalance(ctx, e.ADtoID(v.ToAccount), v.Amount); err != nil {
  3990  			e.log.Error(
  3991  				"Failed to increment balance for account",
  3992  				logging.String("asset", v.ToAccount.AssetID),
  3993  				logging.String("market", v.ToAccount.MarketID),
  3994  				logging.String("owner", v.ToAccount.Owner),
  3995  				logging.String("type", v.ToAccount.Type.String()),
  3996  				logging.BigUint("amount", v.Amount),
  3997  				logging.Error(err),
  3998  			)
  3999  			return nil, err
  4000  		}
  4001  	}
  4002  	lMovements = append(lMovements, gres)
  4003  	// return ledger movements
  4004  	return lMovements, nil
  4005  }
  4006  
  4007  func (e *Engine) SubAccountRelease(
  4008  	ctx context.Context,
  4009  	party, subAccount, asset, market string,
  4010  	pos events.MarketPosition,
  4011  ) ([]*types.LedgerMovement, events.Margin, error) {
  4012  	ownerGeneral, subAccountGeneral, subAccountMargin, err := e.getSubAccounts(subAccount, party, asset, market)
  4013  	if err != nil {
  4014  		return nil, nil, err
  4015  	}
  4016  
  4017  	// subaccount has a position so construct a margin-update thing which we can pass to
  4018  	// the market to closeout its position.
  4019  	var closeout events.Margin
  4020  	if pos != nil && pos.Size() != 0 {
  4021  		closeout = &marginUpdate{
  4022  			MarketPosition:  pos,
  4023  			asset:           asset,
  4024  			marketID:        market,
  4025  			marginShortFall: num.UintZero(),
  4026  		}
  4027  	}
  4028  	// one transfer from general to general
  4029  	treq := &types.TransferRequest{
  4030  		Amount:      subAccountGeneral.Balance.Clone(),
  4031  		MinAmount:   subAccountGeneral.Balance.Clone(),
  4032  		Asset:       asset,
  4033  		Type:        types.TransferTypeAMMRelease,
  4034  		FromAccount: []*types.Account{subAccountGeneral},
  4035  		ToAccount:   []*types.Account{ownerGeneral},
  4036  	}
  4037  
  4038  	gres, err := e.getLedgerEntries(ctx, treq)
  4039  	if err != nil {
  4040  		return nil, nil, err
  4041  	}
  4042  
  4043  	for _, v := range gres.Entries {
  4044  		// increment the to account
  4045  		if err := e.IncrementBalance(ctx, e.ADtoID(v.ToAccount), v.Amount); err != nil {
  4046  			e.log.Error(
  4047  				"Failed to increment balance for account",
  4048  				logging.String("asset", v.ToAccount.AssetID),
  4049  				logging.String("market", v.ToAccount.MarketID),
  4050  				logging.String("owner", v.ToAccount.Owner),
  4051  				logging.String("type", v.ToAccount.Type.String()),
  4052  				logging.BigUint("amount", v.Amount),
  4053  				logging.Error(err),
  4054  			)
  4055  			return nil, nil, err
  4056  		}
  4057  	}
  4058  
  4059  	// if there's no margin balance to release, we're done
  4060  	if subAccountMargin.Balance.IsZero() {
  4061  		return []*types.LedgerMovement{gres}, closeout, nil
  4062  	}
  4063  
  4064  	// non-zero balance -> confiscate
  4065  	// one transfer from margin to market insurance
  4066  	mktInsurance, err := e.GetMarketInsurancePoolAccount(market, asset)
  4067  	if err != nil {
  4068  		return nil, nil, err
  4069  	}
  4070  
  4071  	treq = &types.TransferRequest{
  4072  		Amount:      subAccountMargin.Balance.Clone(),
  4073  		MinAmount:   subAccountMargin.Balance.Clone(),
  4074  		Asset:       asset,
  4075  		Type:        types.TransferTypeAMMRelease,
  4076  		FromAccount: []*types.Account{subAccountMargin},
  4077  		ToAccount:   []*types.Account{mktInsurance},
  4078  	}
  4079  
  4080  	mres, err := e.getLedgerEntries(ctx, treq)
  4081  	if err != nil {
  4082  		return nil, nil, err
  4083  	}
  4084  
  4085  	for _, v := range mres.Entries {
  4086  		// increment the to account
  4087  		if err := e.IncrementBalance(ctx, e.ADtoID(v.ToAccount), v.Amount); err != nil {
  4088  			e.log.Error(
  4089  				"Failed to increment balance for account",
  4090  				logging.String("asset", v.ToAccount.AssetID),
  4091  				logging.String("market", v.ToAccount.MarketID),
  4092  				logging.String("owner", v.ToAccount.Owner),
  4093  				logging.String("type", v.ToAccount.Type.String()),
  4094  				logging.BigUint("amount", v.Amount),
  4095  				logging.Error(err),
  4096  			)
  4097  		}
  4098  	}
  4099  
  4100  	return []*types.LedgerMovement{gres, mres}, closeout, nil
  4101  }
  4102  
  4103  // GetPartyMarginAccount returns a margin account given the partyID and market.
  4104  func (e *Engine) GetPartyMarginAccount(market, party, asset string) (*types.Account, error) {
  4105  	margin := e.accountID(market, party, asset, types.AccountTypeMargin)
  4106  	return e.GetAccountByID(margin)
  4107  }
  4108  
  4109  // GetPartyOrderMarginAccount returns a margin account given the partyID and market.
  4110  func (e *Engine) GetPartyOrderMarginAccount(market, party, asset string) (*types.Account, error) {
  4111  	orderMargin := e.accountID(market, party, asset, types.AccountTypeOrderMargin)
  4112  	return e.GetAccountByID(orderMargin)
  4113  }
  4114  
  4115  // GetPartyHoldingAccount returns a holding account given the partyID and market.
  4116  func (e *Engine) GetPartyHoldingAccount(party, asset string) (*types.Account, error) {
  4117  	margin := e.accountID(noMarket, party, asset, types.AccountTypeHolding)
  4118  	return e.GetAccountByID(margin)
  4119  }
  4120  
  4121  // GetPartyGeneralAccount returns a general account given the partyID.
  4122  func (e *Engine) GetPartyGeneralAccount(partyID, asset string) (*types.Account, error) {
  4123  	generalID := e.accountID(noMarket, partyID, asset, types.AccountTypeGeneral)
  4124  	return e.GetAccountByID(generalID)
  4125  }
  4126  
  4127  // GetPartyLockedForStaking returns a general account given the partyID.
  4128  func (e *Engine) GetPartyLockedForStaking(partyID, asset string) (*types.Account, error) {
  4129  	generalID := e.accountID(noMarket, partyID, asset, types.AccountTypeLockedForStaking)
  4130  	return e.GetAccountByID(generalID)
  4131  }
  4132  
  4133  // GetPartyBondAccount returns a general account given the partyID.
  4134  func (e *Engine) GetPartyBondAccount(market, partyID, asset string) (*types.Account, error) {
  4135  	id := e.accountID(
  4136  		market, partyID, asset, types.AccountTypeBond)
  4137  	return e.GetAccountByID(id)
  4138  }
  4139  
  4140  // GetPartyLiquidityFeeAccount returns a liquidity fee account account given the partyID.
  4141  func (e *Engine) GetPartyLiquidityFeeAccount(market, partyID, asset string) (*types.Account, error) {
  4142  	id := e.accountID(
  4143  		market, partyID, asset, types.AccountTypeLPLiquidityFees)
  4144  	return e.GetAccountByID(id)
  4145  }
  4146  
  4147  // CreatePartyGeneralAccount create the general account for a party.
  4148  func (e *Engine) CreatePartyGeneralAccount(ctx context.Context, partyID, asset string) (string, error) {
  4149  	if !e.AssetExists(asset) {
  4150  		return "", ErrInvalidAssetID
  4151  	}
  4152  
  4153  	generalID := e.accountID(noMarket, partyID, asset, types.AccountTypeGeneral)
  4154  	if _, ok := e.accs[generalID]; !ok {
  4155  		acc := types.Account{
  4156  			ID:       generalID,
  4157  			Asset:    asset,
  4158  			MarketID: noMarket,
  4159  			Balance:  num.UintZero(),
  4160  			Owner:    partyID,
  4161  			Type:     types.AccountTypeGeneral,
  4162  		}
  4163  		e.accs[generalID] = &acc
  4164  		e.addPartyAccount(partyID, generalID, &acc)
  4165  		e.addAccountToHashableSlice(&acc)
  4166  		e.broker.Send(events.NewPartyEvent(ctx, types.Party{Id: partyID}))
  4167  		e.broker.Send(events.NewAccountEvent(ctx, acc))
  4168  	}
  4169  
  4170  	return generalID, nil
  4171  }
  4172  
  4173  // CreatePartyLockedForStakingAccount create the general account for a party.
  4174  func (e *Engine) CreatePartyLockedForStakingAccount(ctx context.Context, partyID, asset string) (string, error) {
  4175  	if !e.AssetExists(asset) {
  4176  		return "", ErrInvalidAssetID
  4177  	}
  4178  
  4179  	lockedForStakingID := e.accountID(noMarket, partyID, asset, types.AccountTypeLockedForStaking)
  4180  	if _, ok := e.accs[lockedForStakingID]; !ok {
  4181  		acc := types.Account{
  4182  			ID:       lockedForStakingID,
  4183  			Asset:    asset,
  4184  			MarketID: noMarket,
  4185  			Balance:  num.UintZero(),
  4186  			Owner:    partyID,
  4187  			Type:     types.AccountTypeLockedForStaking,
  4188  		}
  4189  		e.accs[lockedForStakingID] = &acc
  4190  		e.addPartyAccount(partyID, lockedForStakingID, &acc)
  4191  		e.addAccountToHashableSlice(&acc)
  4192  		e.broker.Send(events.NewPartyEvent(ctx, types.Party{Id: partyID}))
  4193  		e.broker.Send(events.NewAccountEvent(ctx, acc))
  4194  	}
  4195  
  4196  	return lockedForStakingID, nil
  4197  }
  4198  
  4199  // GetOrCreatePartyVestingRewardAccount create the general account for a party.
  4200  func (e *Engine) GetOrCreatePartyVestingRewardAccount(ctx context.Context, partyID, asset string) *types.Account {
  4201  	if !e.AssetExists(asset) {
  4202  		e.log.Panic("trying to use a non-existent asset for reward accounts, something went very wrong somewhere",
  4203  			logging.String("asset-id", asset))
  4204  	}
  4205  
  4206  	id := e.accountID(noMarket, partyID, asset, types.AccountTypeVestingRewards)
  4207  	acc, ok := e.accs[id]
  4208  	if !ok {
  4209  		acc = &types.Account{
  4210  			ID:       id,
  4211  			Asset:    asset,
  4212  			MarketID: noMarket,
  4213  			Balance:  num.UintZero(),
  4214  			Owner:    partyID,
  4215  			Type:     types.AccountTypeVestingRewards,
  4216  		}
  4217  		e.accs[id] = acc
  4218  		e.addPartyAccount(partyID, id, acc)
  4219  		e.addAccountToHashableSlice(acc)
  4220  		e.broker.Send(events.NewPartyEvent(ctx, types.Party{Id: partyID}))
  4221  		e.broker.Send(events.NewAccountEvent(ctx, *acc))
  4222  	}
  4223  
  4224  	return acc.Clone()
  4225  }
  4226  
  4227  func (e *Engine) GetPartyVestedRewardAccount(partyID, asset string) (*types.Account, error) {
  4228  	vested := e.accountID(noMarket, partyID, asset, types.AccountTypeVestedRewards)
  4229  	return e.GetAccountByID(vested)
  4230  }
  4231  
  4232  // GetOrCreatePartyVestedRewardAccount create the general account for a party.
  4233  func (e *Engine) GetOrCreatePartyVestedRewardAccount(ctx context.Context, partyID, asset string) *types.Account {
  4234  	if !e.AssetExists(asset) {
  4235  		e.log.Panic("trying to use a nonexisting asset for reward accounts, something went very wrong somewhere",
  4236  			logging.String("asset-id", asset))
  4237  	}
  4238  
  4239  	id := e.accountID(noMarket, partyID, asset, types.AccountTypeVestedRewards)
  4240  	acc, ok := e.accs[id]
  4241  	if !ok {
  4242  		acc = &types.Account{
  4243  			ID:       id,
  4244  			Asset:    asset,
  4245  			MarketID: noMarket,
  4246  			Balance:  num.UintZero(),
  4247  			Owner:    partyID,
  4248  			Type:     types.AccountTypeVestedRewards,
  4249  		}
  4250  		e.accs[id] = acc
  4251  		e.addPartyAccount(partyID, id, acc)
  4252  		e.addAccountToHashableSlice(acc)
  4253  		e.broker.Send(events.NewPartyEvent(ctx, types.Party{Id: partyID}))
  4254  		e.broker.Send(events.NewAccountEvent(ctx, *acc))
  4255  	}
  4256  
  4257  	return acc.Clone()
  4258  }
  4259  
  4260  // RemoveDistressed will remove all distressed party in the event positions
  4261  // for a given market and asset.
  4262  func (e *Engine) RemoveDistressed(ctx context.Context, parties []events.MarketPosition, marketID, asset string, useGeneralAccount func(string) bool) (*types.LedgerMovement, error) {
  4263  	tl := len(parties)
  4264  	if tl == 0 {
  4265  		return nil, nil
  4266  	}
  4267  	// insurance account is the one we're after
  4268  	_, ins, err := e.getSystemAccounts(marketID, asset)
  4269  	if err != nil {
  4270  		return nil, err
  4271  	}
  4272  	resp := types.LedgerMovement{
  4273  		Entries: make([]*types.LedgerEntry, 0, tl),
  4274  	}
  4275  	now := e.timeService.GetTimeNow().UnixNano()
  4276  	for _, party := range parties {
  4277  		bondAcc, err := e.GetAccountByID(e.accountID(marketID, party.Party(), asset, types.AccountTypeBond))
  4278  		if err != nil {
  4279  			bondAcc = &types.Account{}
  4280  		}
  4281  		genAcc, err := e.GetAccountByID(e.accountID(noMarket, party.Party(), asset, types.AccountTypeGeneral))
  4282  		if err != nil {
  4283  			return nil, err
  4284  		}
  4285  		marginAcc, err := e.GetAccountByID(e.accountID(marketID, party.Party(), asset, types.AccountTypeMargin))
  4286  		if err != nil {
  4287  			return nil, err
  4288  		}
  4289  		// If any balance remains on bond account, move it over to margin account
  4290  		if bondAcc.Balance != nil && !bondAcc.Balance.IsZero() {
  4291  			resp.Entries = append(resp.Entries, &types.LedgerEntry{
  4292  				FromAccount: bondAcc.ToDetails(),
  4293  				ToAccount:   marginAcc.ToDetails(),
  4294  				Amount:      bondAcc.Balance.Clone(),
  4295  				Type:        types.TransferTypeMarginLow,
  4296  				// Reference:   "position-resolution",
  4297  				Timestamp:          now,
  4298  				FromAccountBalance: num.UintZero(),
  4299  				ToAccountBalance:   num.Sum(bondAcc.Balance, marginAcc.Balance),
  4300  			})
  4301  			if err := e.IncrementBalance(ctx, marginAcc.ID, bondAcc.Balance); err != nil {
  4302  				return nil, err
  4303  			}
  4304  			if err := e.UpdateBalance(ctx, bondAcc.ID, bondAcc.Balance.SetUint64(0)); err != nil {
  4305  				return nil, err
  4306  			}
  4307  			marginAcc, _ = e.GetAccountByID(e.accountID(marketID, party.Party(), asset, types.AccountTypeMargin))
  4308  		}
  4309  		// take whatever is left on the general account, and move to margin balance
  4310  		// we can take everything from the account, as whatever amount was left here didn't cover the minimum margin requirement
  4311  		if useGeneralAccount(party.Party()) && genAcc.Balance != nil && !genAcc.Balance.IsZero() {
  4312  			resp.Entries = append(resp.Entries, &types.LedgerEntry{
  4313  				FromAccount: genAcc.ToDetails(),
  4314  				ToAccount:   marginAcc.ToDetails(),
  4315  				Amount:      genAcc.Balance.Clone(),
  4316  				Type:        types.TransferTypeMarginLow,
  4317  				// Reference:   "position-resolution",
  4318  				Timestamp:          now,
  4319  				FromAccountBalance: num.UintZero(),
  4320  				ToAccountBalance:   num.Sum(marginAcc.Balance, genAcc.Balance),
  4321  			})
  4322  			if err := e.IncrementBalance(ctx, marginAcc.ID, genAcc.Balance); err != nil {
  4323  				return nil, err
  4324  			}
  4325  			if err := e.UpdateBalance(ctx, genAcc.ID, genAcc.Balance.SetUint64(0)); err != nil {
  4326  				return nil, err
  4327  			}
  4328  			marginAcc, _ = e.GetAccountByID(e.accountID(marketID, party.Party(), asset, types.AccountTypeMargin))
  4329  		}
  4330  		// move monies from the margin account (balance is general, bond, and margin combined now)
  4331  		if !marginAcc.Balance.IsZero() {
  4332  			resp.Entries = append(resp.Entries, &types.LedgerEntry{
  4333  				FromAccount: marginAcc.ToDetails(),
  4334  				ToAccount:   ins.ToDetails(),
  4335  				Amount:      marginAcc.Balance.Clone(),
  4336  				Type:        types.TransferTypeMarginConfiscated,
  4337  				// Reference:   "position-resolution",
  4338  				Timestamp:          now,
  4339  				FromAccountBalance: num.UintZero(),
  4340  				ToAccountBalance:   num.Sum(ins.Balance, marginAcc.Balance),
  4341  			})
  4342  			if err := e.IncrementBalance(ctx, ins.ID, marginAcc.Balance); err != nil {
  4343  				return nil, err
  4344  			}
  4345  			if err := e.UpdateBalance(ctx, marginAcc.ID, marginAcc.Balance.SetUint64(0)); err != nil {
  4346  				return nil, err
  4347  			}
  4348  		}
  4349  
  4350  		// we remove the margin account
  4351  		if useGeneralAccount(party.Party()) {
  4352  			e.removeAccount(marginAcc.ID)
  4353  			// remove account from balances tracking
  4354  			e.rmPartyAccount(party.Party(), marginAcc.ID)
  4355  		}
  4356  	}
  4357  	return &resp, nil
  4358  }
  4359  
  4360  func (e *Engine) ClearPartyOrderMarginAccount(ctx context.Context, party, market, asset string) (*types.LedgerMovement, error) {
  4361  	acc, err := e.GetAccountByID(e.accountID(market, party, asset, types.AccountTypeOrderMargin))
  4362  	if err != nil {
  4363  		return nil, err
  4364  	}
  4365  	return e.clearPartyMarginAccount(ctx, party, asset, acc, types.TransferTypeOrderMarginHigh)
  4366  }
  4367  
  4368  func (e *Engine) ClearPartyMarginAccount(ctx context.Context, party, market, asset string) (*types.LedgerMovement, error) {
  4369  	acc, err := e.GetAccountByID(e.accountID(market, party, asset, types.AccountTypeMargin))
  4370  	if err != nil {
  4371  		return nil, err
  4372  	}
  4373  	return e.clearPartyMarginAccount(ctx, party, asset, acc, types.TransferTypeMarginHigh)
  4374  }
  4375  
  4376  func (e *Engine) clearPartyMarginAccount(ctx context.Context, party, asset string, acc *types.Account, transferType types.TransferType) (*types.LedgerMovement, error) {
  4377  	// preevent returning empty ledger movements
  4378  	if acc.Balance.IsZero() {
  4379  		return nil, nil
  4380  	}
  4381  
  4382  	resp := types.LedgerMovement{
  4383  		Entries: []*types.LedgerEntry{},
  4384  	}
  4385  	now := e.timeService.GetTimeNow().UnixNano()
  4386  
  4387  	genAcc, err := e.GetAccountByID(e.accountID(noMarket, party, asset, types.AccountTypeGeneral))
  4388  	if err != nil {
  4389  		return nil, err
  4390  	}
  4391  
  4392  	resp.Entries = append(resp.Entries, &types.LedgerEntry{
  4393  		FromAccount:        acc.ToDetails(),
  4394  		ToAccount:          genAcc.ToDetails(),
  4395  		Amount:             acc.Balance.Clone(),
  4396  		Type:               transferType,
  4397  		Timestamp:          now,
  4398  		FromAccountBalance: num.UintZero(),
  4399  		ToAccountBalance:   num.Sum(genAcc.Balance, acc.Balance),
  4400  	})
  4401  	if err := e.IncrementBalance(ctx, genAcc.ID, acc.Balance); err != nil {
  4402  		return nil, err
  4403  	}
  4404  	if err := e.UpdateBalance(ctx, acc.ID, acc.Balance.SetUint64(0)); err != nil {
  4405  		return nil, err
  4406  	}
  4407  
  4408  	return &resp, nil
  4409  }
  4410  
  4411  // CreateMarketAccounts will create all required accounts for a market once
  4412  // a new market is accepted through the network.
  4413  func (e *Engine) CreateMarketAccounts(ctx context.Context, marketID, asset string) (insuranceID, settleID string, err error) {
  4414  	if !e.AssetExists(asset) {
  4415  		return "", "", ErrInvalidAssetID
  4416  	}
  4417  	insuranceID = e.accountID(marketID, "", asset, types.AccountTypeInsurance)
  4418  	_, ok := e.accs[insuranceID]
  4419  	if !ok {
  4420  		insAcc := &types.Account{
  4421  			ID:       insuranceID,
  4422  			Asset:    asset,
  4423  			Owner:    systemOwner,
  4424  			Balance:  num.UintZero(),
  4425  			MarketID: marketID,
  4426  			Type:     types.AccountTypeInsurance,
  4427  		}
  4428  		e.accs[insuranceID] = insAcc
  4429  		e.addAccountToHashableSlice(insAcc)
  4430  		e.broker.Send(events.NewAccountEvent(ctx, *insAcc))
  4431  	}
  4432  	settleID = e.accountID(marketID, "", asset, types.AccountTypeSettlement)
  4433  	_, ok = e.accs[settleID]
  4434  	if !ok {
  4435  		setAcc := &types.Account{
  4436  			ID:       settleID,
  4437  			Asset:    asset,
  4438  			Owner:    systemOwner,
  4439  			Balance:  num.UintZero(),
  4440  			MarketID: marketID,
  4441  			Type:     types.AccountTypeSettlement,
  4442  		}
  4443  		e.accs[settleID] = setAcc
  4444  		e.addAccountToHashableSlice(setAcc)
  4445  		e.broker.Send(events.NewAccountEvent(ctx, *setAcc))
  4446  	}
  4447  
  4448  	// these are fee related account only
  4449  	liquidityFeeID := e.accountID(marketID, "", asset, types.AccountTypeFeesLiquidity)
  4450  	_, ok = e.accs[liquidityFeeID]
  4451  	if !ok {
  4452  		liquidityFeeAcc := &types.Account{
  4453  			ID:       liquidityFeeID,
  4454  			Asset:    asset,
  4455  			Owner:    systemOwner,
  4456  			Balance:  num.UintZero(),
  4457  			MarketID: marketID,
  4458  			Type:     types.AccountTypeFeesLiquidity,
  4459  		}
  4460  		e.accs[liquidityFeeID] = liquidityFeeAcc
  4461  		e.addAccountToHashableSlice(liquidityFeeAcc)
  4462  		e.broker.Send(events.NewAccountEvent(ctx, *liquidityFeeAcc))
  4463  	}
  4464  	makerFeeID := e.accountID(marketID, "", asset, types.AccountTypeFeesMaker)
  4465  	_, ok = e.accs[makerFeeID]
  4466  	if !ok {
  4467  		makerFeeAcc := &types.Account{
  4468  			ID:       makerFeeID,
  4469  			Asset:    asset,
  4470  			Owner:    systemOwner,
  4471  			Balance:  num.UintZero(),
  4472  			MarketID: marketID,
  4473  			Type:     types.AccountTypeFeesMaker,
  4474  		}
  4475  		e.accs[makerFeeID] = makerFeeAcc
  4476  		e.addAccountToHashableSlice(makerFeeAcc)
  4477  		e.broker.Send(events.NewAccountEvent(ctx, *makerFeeAcc))
  4478  	}
  4479  
  4480  	_, err = e.GetOrCreateLiquidityFeesBonusDistributionAccount(ctx, marketID, asset)
  4481  
  4482  	return insuranceID, settleID, err
  4483  }
  4484  
  4485  func (e *Engine) HasGeneralAccount(party, asset string) bool {
  4486  	_, err := e.GetAccountByID(
  4487  		e.accountID(noMarket, party, asset, types.AccountTypeGeneral))
  4488  	return err == nil
  4489  }
  4490  
  4491  // Withdraw will remove the specified amount from the party
  4492  // general account.
  4493  func (e *Engine) Withdraw(ctx context.Context, partyID, asset string, amount *num.Uint) (*types.LedgerMovement, error) {
  4494  	if !e.AssetExists(asset) {
  4495  		return nil, ErrInvalidAssetID
  4496  	}
  4497  	acc, err := e.GetAccountByID(e.accountID(noMarket, partyID, asset, types.AccountTypeGeneral))
  4498  	if err != nil {
  4499  		return nil, ErrAccountDoesNotExist
  4500  	}
  4501  	if amount.GT(acc.Balance) {
  4502  		return nil, ErrNotEnoughFundsToWithdraw
  4503  	}
  4504  
  4505  	transf := types.Transfer{
  4506  		Owner: partyID,
  4507  		Amount: &types.FinancialAmount{
  4508  			Amount: amount.Clone(),
  4509  			Asset:  asset,
  4510  		},
  4511  		Type:      types.TransferTypeWithdraw,
  4512  		MinAmount: amount.Clone(),
  4513  	}
  4514  	// @TODO ensure this is safe!
  4515  	mEvt := marginUpdate{
  4516  		general: acc,
  4517  	}
  4518  	req, err := e.getTransferRequest(&transf, nil, nil, &mEvt, true)
  4519  	if err != nil {
  4520  		return nil, err
  4521  	}
  4522  
  4523  	res, err := e.getLedgerEntries(ctx, req)
  4524  	if err != nil {
  4525  		return nil, err
  4526  	}
  4527  	for _, bal := range res.Balances {
  4528  		if err := e.IncrementBalance(ctx, bal.Account.ID, bal.Balance); err != nil {
  4529  			e.log.Error("Could not update the target account in transfer",
  4530  				logging.String("account-id", bal.Account.ID),
  4531  				logging.Error(err))
  4532  			return nil, err
  4533  		}
  4534  	}
  4535  
  4536  	return res, nil
  4537  }
  4538  
  4539  // RestoreCheckpointBalance will credit account with a balance from
  4540  // a checkpoint. This function assume the accounts have been created
  4541  // before.
  4542  func (e *Engine) RestoreCheckpointBalance(
  4543  	ctx context.Context,
  4544  	market, party, asset string,
  4545  	typ types.AccountType,
  4546  	amount *num.Uint,
  4547  ) (*types.LedgerMovement, error) {
  4548  	treq := &types.TransferRequest{
  4549  		Amount:    amount.Clone(),
  4550  		MinAmount: amount.Clone(),
  4551  		Asset:     asset,
  4552  		Type:      types.TransferTypeCheckpointBalanceRestore,
  4553  	}
  4554  
  4555  	// first get the external account and ensure the funds there are OK
  4556  	eacc, err := e.GetAccountByID(
  4557  		e.accountID(noMarket, systemOwner, asset, types.AccountTypeExternal))
  4558  	if err != nil {
  4559  		e.log.Panic(
  4560  			"Failed to get the asset external account",
  4561  			logging.String("asset", asset),
  4562  			logging.Error(err),
  4563  		)
  4564  	}
  4565  	eacc.Balance = eacc.Balance.Add(eacc.Balance, amount.Clone())
  4566  	treq.FromAccount = []*types.Account{
  4567  		eacc,
  4568  	}
  4569  
  4570  	// get our destination account
  4571  	acc, _ := e.GetAccountByID(e.accountID(market, party, asset, typ))
  4572  	treq.ToAccount = []*types.Account{
  4573  		acc,
  4574  	}
  4575  
  4576  	lms, err := e.getLedgerEntries(ctx, treq)
  4577  	if err != nil {
  4578  		return nil, err
  4579  	}
  4580  	for _, bal := range lms.Balances {
  4581  		if err := e.IncrementBalance(ctx, bal.Account.ID, bal.Balance); err != nil {
  4582  			e.log.Error("Could not update the target account in transfer",
  4583  				logging.String("account-id", bal.Account.ID),
  4584  				logging.Error(err))
  4585  			return nil, err
  4586  		}
  4587  	}
  4588  
  4589  	return lms, nil
  4590  }
  4591  
  4592  // Deposit will deposit the given amount into the party account.
  4593  func (e *Engine) Deposit(ctx context.Context, partyID, asset string, amount *num.Uint) (*types.LedgerMovement, error) {
  4594  	if !e.AssetExists(asset) {
  4595  		return nil, ErrInvalidAssetID
  4596  	}
  4597  	var accID string
  4598  	var err error
  4599  	// Look for the special reward party
  4600  	if partyID == rewardPartyID {
  4601  		acc, err := e.GetGlobalRewardAccount(asset)
  4602  		if err != nil {
  4603  			return nil, err
  4604  		}
  4605  		accID = acc.ID
  4606  	} else {
  4607  		// this will get or create the account basically
  4608  		accID, err = e.CreatePartyGeneralAccount(ctx, partyID, asset)
  4609  		if err != nil {
  4610  			return nil, err
  4611  		}
  4612  	}
  4613  	acc, _ := e.GetAccountByID(accID)
  4614  	transf := types.Transfer{
  4615  		Owner: partyID,
  4616  		Amount: &types.FinancialAmount{
  4617  			Amount: amount.Clone(),
  4618  			Asset:  asset,
  4619  		},
  4620  		Type:      types.TransferTypeDeposit,
  4621  		MinAmount: amount.Clone(),
  4622  	}
  4623  
  4624  	// @TODO -> again, is this safe?
  4625  	mEvt := marginUpdate{
  4626  		general: acc,
  4627  	}
  4628  
  4629  	req, err := e.getTransferRequest(&transf, nil, nil, &mEvt, true)
  4630  	if err != nil {
  4631  		return nil, err
  4632  	}
  4633  	res, err := e.getLedgerEntries(ctx, req)
  4634  	if err != nil {
  4635  		return nil, err
  4636  	}
  4637  	for _, bal := range res.Balances {
  4638  		if err := e.IncrementBalance(ctx, bal.Account.ID, bal.Balance); err != nil {
  4639  			e.log.Error("Could not update the target account in transfer",
  4640  				logging.String("account-id", bal.Account.ID),
  4641  				logging.Error(err))
  4642  			return nil, err
  4643  		}
  4644  	}
  4645  
  4646  	return res, nil
  4647  }
  4648  
  4649  // UpdateBalance will update the balance of a given account.
  4650  func (e *Engine) UpdateBalance(ctx context.Context, id string, balance *num.Uint) error {
  4651  	acc, ok := e.accs[id]
  4652  	if !ok {
  4653  		return ErrAccountDoesNotExist
  4654  	}
  4655  	acc.Balance.Set(balance)
  4656  	// update
  4657  	if acc.Type != types.AccountTypeExternal {
  4658  		e.state.updateAccs(e.hashableAccs)
  4659  		e.broker.Send(events.NewAccountEvent(ctx, *acc))
  4660  	}
  4661  
  4662  	return nil
  4663  }
  4664  
  4665  // IncrementBalance will increment the balance of a given account
  4666  // using the given value.
  4667  func (e *Engine) IncrementBalance(ctx context.Context, id string, inc *num.Uint) error {
  4668  	acc, ok := e.accs[id]
  4669  	if !ok {
  4670  		return fmt.Errorf("account does not exist: %s", id)
  4671  	}
  4672  	acc.Balance.AddSum(inc)
  4673  	if acc.Type != types.AccountTypeExternal {
  4674  		e.state.updateAccs(e.hashableAccs)
  4675  		e.broker.Send(events.NewAccountEvent(ctx, *acc))
  4676  	}
  4677  
  4678  	return nil
  4679  }
  4680  
  4681  // GetAccountByID will return an account using the given id.
  4682  func (e *Engine) GetAccountByID(id string) (*types.Account, error) {
  4683  	acc, ok := e.accs[id]
  4684  
  4685  	if !ok {
  4686  		return nil, fmt.Errorf("account does not exist: %s", id)
  4687  	}
  4688  	return acc.Clone(), nil
  4689  }
  4690  
  4691  // GetEnabledAssets returns the asset IDs of all enabled assets.
  4692  func (e *Engine) GetEnabledAssets() []string {
  4693  	assets := make([]string, 0, len(e.enabledAssets))
  4694  	for _, asset := range e.enabledAssets {
  4695  		assets = append(assets, asset.ID)
  4696  	}
  4697  	sort.Strings(assets)
  4698  	return assets
  4699  }
  4700  
  4701  func (e *Engine) removeAccount(id string) {
  4702  	delete(e.accs, id)
  4703  	e.removeAccountFromHashableSlice(id)
  4704  }
  4705  
  4706  func (e *Engine) ADtoID(ad *types.AccountDetails) string {
  4707  	return e.accountID(ad.MarketID, ad.Owner, ad.AssetID, ad.Type)
  4708  }
  4709  
  4710  // @TODO this function uses a single slice for each call. This is fine now, as we're processing
  4711  // everything sequentially, and so there's no possible data-races here. Once we start doing things
  4712  // like cleaning up expired markets asynchronously, then this func is not safe for concurrent use.
  4713  func (e *Engine) accountID(marketID, partyID, asset string, ty types.AccountType) string {
  4714  	if len(marketID) <= 0 {
  4715  		marketID = noMarket
  4716  	}
  4717  
  4718  	// market account
  4719  	if len(partyID) <= 0 {
  4720  		partyID = systemOwner
  4721  	}
  4722  
  4723  	copy(e.idbuf, marketID)
  4724  	ln := len(marketID)
  4725  	copy(e.idbuf[ln:], partyID)
  4726  	ln += len(partyID)
  4727  	copy(e.idbuf[ln:], asset)
  4728  	ln += len(asset)
  4729  	e.idbuf[ln] = byte(ty + 48)
  4730  	return string(e.idbuf[:ln+1])
  4731  }
  4732  
  4733  func (e *Engine) GetMarketLiquidityFeeAccount(market, asset string) (*types.Account, error) {
  4734  	liquidityAccID := e.accountID(market, systemOwner, asset, types.AccountTypeFeesLiquidity)
  4735  	return e.GetAccountByID(liquidityAccID)
  4736  }
  4737  
  4738  func (e *Engine) GetMarketMakerFeeAccount(market, asset string) (*types.Account, error) {
  4739  	makerAccID := e.accountID(market, systemOwner, asset, types.AccountTypeFeesMaker)
  4740  	return e.GetAccountByID(makerAccID)
  4741  }
  4742  
  4743  func (e *Engine) GetMarketInsurancePoolAccount(market, asset string) (*types.Account, error) {
  4744  	insuranceAccID := e.accountID(market, systemOwner, asset, types.AccountTypeInsurance)
  4745  	return e.GetAccountByID(insuranceAccID)
  4746  }
  4747  
  4748  func (e *Engine) GetOrCreateMarketInsurancePoolAccount(ctx context.Context, market, asset string) *types.Account {
  4749  	insuranceAccID := e.accountID(market, systemOwner, asset, types.AccountTypeInsurance)
  4750  	acc, err := e.GetAccountByID(insuranceAccID)
  4751  	if err != nil {
  4752  		acc = &types.Account{
  4753  			ID:       insuranceAccID,
  4754  			Asset:    asset,
  4755  			Owner:    systemOwner,
  4756  			Balance:  num.UintZero(),
  4757  			MarketID: market,
  4758  			Type:     types.AccountTypeInsurance,
  4759  		}
  4760  		e.accs[insuranceAccID] = acc
  4761  		e.addAccountToHashableSlice(acc)
  4762  		// not sure if we should send this event, but in case this account was never created, we probably should make sure the datanode
  4763  		// is aware of it. This is most likely only ever going to be called in unit tests, though.
  4764  		e.broker.Send(events.NewAccountEvent(ctx, *acc))
  4765  	}
  4766  	return acc
  4767  }
  4768  
  4769  func (e *Engine) GetGlobalRewardAccount(asset string) (*types.Account, error) {
  4770  	rewardAccID := e.accountID(noMarket, systemOwner, asset, types.AccountTypeGlobalReward)
  4771  	return e.GetAccountByID(rewardAccID)
  4772  }
  4773  
  4774  func (e *Engine) GetNetworkTreasuryAccount(asset string) (*types.Account, error) {
  4775  	return e.GetAccountByID(e.accountID(noMarket, systemOwner, asset, types.AccountTypeNetworkTreasury))
  4776  }
  4777  
  4778  func (e *Engine) GetOrCreateBuyBackFeesAccountID(ctx context.Context, asset string) string {
  4779  	return e.getOrCreateBuyBackFeesAccount(ctx, asset).ID
  4780  }
  4781  
  4782  func (e *Engine) getOrCreateBuyBackFeesAccount(ctx context.Context, asset string) *types.Account {
  4783  	accID := e.accountID(noMarket, systemOwner, asset, types.AccountTypeBuyBackFees)
  4784  	acc, err := e.GetAccountByID(accID)
  4785  	if err == nil {
  4786  		return acc
  4787  	}
  4788  	ntAcc := &types.Account{
  4789  		ID:       accID,
  4790  		Asset:    asset,
  4791  		Owner:    systemOwner,
  4792  		Balance:  num.UintZero(),
  4793  		MarketID: noMarket,
  4794  		Type:     types.AccountTypeBuyBackFees,
  4795  	}
  4796  	e.accs[accID] = ntAcc
  4797  	e.addAccountToHashableSlice(ntAcc)
  4798  	e.broker.Send(events.NewAccountEvent(ctx, *ntAcc))
  4799  	return ntAcc
  4800  }
  4801  
  4802  func (e *Engine) GetOrCreateNetworkTreasuryAccount(ctx context.Context, asset string) *types.Account {
  4803  	accID := e.accountID(noMarket, systemOwner, asset, types.AccountTypeNetworkTreasury)
  4804  	acc, err := e.GetAccountByID(accID)
  4805  	if err == nil {
  4806  		return acc
  4807  	}
  4808  	ntAcc := &types.Account{
  4809  		ID:       accID,
  4810  		Asset:    asset,
  4811  		Owner:    systemOwner,
  4812  		Balance:  num.UintZero(),
  4813  		MarketID: noMarket,
  4814  		Type:     types.AccountTypeNetworkTreasury,
  4815  	}
  4816  	e.accs[accID] = ntAcc
  4817  	e.addAccountToHashableSlice(ntAcc)
  4818  	e.broker.Send(events.NewAccountEvent(ctx, *ntAcc))
  4819  	return ntAcc
  4820  }
  4821  
  4822  func (e *Engine) getOrCreateNetworkAccount(ctx context.Context, asset string, accountType types.AccountType) *types.Account {
  4823  	accID := e.accountID(noMarket, systemOwner, asset, accountType)
  4824  	acc, err := e.GetAccountByID(accID)
  4825  	if err == nil {
  4826  		return acc
  4827  	}
  4828  	ntAcc := &types.Account{
  4829  		ID:       accID,
  4830  		Asset:    asset,
  4831  		Owner:    systemOwner,
  4832  		Balance:  num.UintZero(),
  4833  		MarketID: noMarket,
  4834  		Type:     accountType,
  4835  	}
  4836  	e.accs[accID] = ntAcc
  4837  	e.addAccountToHashableSlice(ntAcc)
  4838  	e.broker.Send(events.NewAccountEvent(ctx, *ntAcc))
  4839  	return ntAcc
  4840  }
  4841  
  4842  func (e *Engine) GetGlobalInsuranceAccount(asset string) (*types.Account, error) {
  4843  	return e.GetAccountByID(e.accountID(noMarket, systemOwner, asset, types.AccountTypeGlobalInsurance))
  4844  }
  4845  
  4846  func (e *Engine) GetOrCreateGlobalInsuranceAccount(ctx context.Context, asset string) *types.Account {
  4847  	accID := e.accountID(noMarket, systemOwner, asset, types.AccountTypeGlobalInsurance)
  4848  	acc, err := e.GetAccountByID(accID)
  4849  	if err == nil {
  4850  		return acc
  4851  	}
  4852  	giAcc := &types.Account{
  4853  		ID:       accID,
  4854  		Asset:    asset,
  4855  		Owner:    systemOwner,
  4856  		Balance:  num.UintZero(),
  4857  		MarketID: noMarket,
  4858  		Type:     types.AccountTypeGlobalInsurance,
  4859  	}
  4860  	e.accs[accID] = giAcc
  4861  	e.addAccountToHashableSlice(giAcc)
  4862  	e.broker.Send(events.NewAccountEvent(ctx, *giAcc))
  4863  	return giAcc
  4864  }
  4865  
  4866  // GetRewardAccount returns a reward accound by asset and type.
  4867  func (e *Engine) GetOrCreateRewardAccount(ctx context.Context, asset string, market string, rewardAcccountType types.AccountType) (*types.Account, error) {
  4868  	rewardID := e.accountID(market, systemOwner, asset, rewardAcccountType)
  4869  	acc, err := e.GetAccountByID(rewardID)
  4870  	if err == nil {
  4871  		return acc, nil
  4872  	}
  4873  
  4874  	if _, ok := e.accs[rewardID]; !ok {
  4875  		rewardAcc := &types.Account{
  4876  			ID:       rewardID,
  4877  			Asset:    asset,
  4878  			Owner:    systemOwner,
  4879  			Balance:  num.UintZero(),
  4880  			MarketID: market,
  4881  			Type:     rewardAcccountType,
  4882  		}
  4883  		e.accs[rewardID] = rewardAcc
  4884  		e.addAccountToHashableSlice(rewardAcc)
  4885  		e.broker.Send(events.NewAccountEvent(ctx, *rewardAcc))
  4886  	}
  4887  	return e.GetAccountByID(rewardID)
  4888  }
  4889  
  4890  func (e *Engine) GetAssetQuantum(asset string) (num.Decimal, error) {
  4891  	if !e.AssetExists(asset) {
  4892  		return num.DecimalZero(), ErrInvalidAssetID
  4893  	}
  4894  	return e.enabledAssets[asset].Details.Quantum, nil
  4895  }
  4896  
  4897  func (e *Engine) GetRewardAccountsByType(rewardAcccountType types.AccountType) []*types.Account {
  4898  	accounts := []*types.Account{}
  4899  	for _, a := range e.hashableAccs {
  4900  		if a.Type == rewardAcccountType {
  4901  			accounts = append(accounts, a.Clone())
  4902  		}
  4903  	}
  4904  	return accounts
  4905  }
  4906  
  4907  func (e *Engine) GetSystemAccountBalance(asset, market string, accountType types.AccountType) (*num.Uint, error) {
  4908  	account, err := e.GetAccountByID(e.accountID(market, systemOwner, asset, accountType))
  4909  	if err != nil {
  4910  		return nil, err
  4911  	}
  4912  	return account.Balance.Clone(), nil
  4913  }
  4914  
  4915  // TransferToHoldingAccount locks funds from accountTypeFrom into holding account account of the party.
  4916  func (e *Engine) TransferToHoldingAccount(ctx context.Context, transfer *types.Transfer, accountTypeFrom types.AccountType) (*types.LedgerMovement, error) {
  4917  	party := transfer.Owner
  4918  	if party == types.NetworkParty {
  4919  		party = systemOwner
  4920  	}
  4921  	sourceAccountID := e.accountID(transfer.Market, party, transfer.Amount.Asset, accountTypeFrom)
  4922  	sourceAccount, err := e.GetAccountByID(sourceAccountID)
  4923  	if err != nil {
  4924  		return nil, err
  4925  	}
  4926  
  4927  	holdingAccountID := e.accountID(noMarket, party, transfer.Amount.Asset, types.AccountTypeHolding)
  4928  	holdingAccount, err := e.GetAccountByID(holdingAccountID)
  4929  	if err != nil {
  4930  		// if the holding account doesn't exist yet we create it here
  4931  		holdingAccountID, err := e.CreatePartyHoldingAccount(ctx, party, transfer.Amount.Asset)
  4932  		if err != nil {
  4933  			return nil, err
  4934  		}
  4935  		holdingAccount, _ = e.GetAccountByID(holdingAccountID)
  4936  	}
  4937  
  4938  	req := &types.TransferRequest{
  4939  		Amount:      transfer.Amount.Amount.Clone(),
  4940  		MinAmount:   transfer.Amount.Amount.Clone(),
  4941  		Asset:       transfer.Amount.Asset,
  4942  		Type:        types.TransferTypeHoldingAccount,
  4943  		FromAccount: []*types.Account{sourceAccount},
  4944  		ToAccount:   []*types.Account{holdingAccount},
  4945  	}
  4946  
  4947  	res, err := e.getLedgerEntries(ctx, req)
  4948  	if err != nil {
  4949  		e.log.Error("Failed to transfer funds", logging.Error(err))
  4950  		return nil, err
  4951  	}
  4952  	for _, bal := range res.Balances {
  4953  		if err := e.IncrementBalance(ctx, bal.Account.ID, bal.Balance); err != nil {
  4954  			e.log.Error("Could not update the target account in transfer",
  4955  				logging.String("account-id", bal.Account.ID),
  4956  				logging.Error(err))
  4957  			return nil, err
  4958  		}
  4959  	}
  4960  	return res, nil
  4961  }
  4962  
  4963  // ReleaseFromHoldingAccount releases locked funds from holding account back to the toAccount of the party.
  4964  func (e *Engine) ReleaseFromHoldingAccount(ctx context.Context, transfer *types.Transfer, toAccountType types.AccountType) (*types.LedgerMovement, error) {
  4965  	party := transfer.Owner
  4966  	if party == types.NetworkParty {
  4967  		party = systemOwner
  4968  	}
  4969  	holdingAccountID := e.accountID(noMarket, party, transfer.Amount.Asset, types.AccountTypeHolding)
  4970  	holdingAccount, err := e.GetAccountByID(holdingAccountID)
  4971  	if err != nil {
  4972  		return nil, err
  4973  	}
  4974  
  4975  	targetAccount, err := e.GetAccountByID(e.accountID(noMarket, party, transfer.Amount.Asset, toAccountType))
  4976  	if err != nil {
  4977  		return nil, err
  4978  	}
  4979  
  4980  	req := &types.TransferRequest{
  4981  		Amount:      transfer.Amount.Amount.Clone(),
  4982  		MinAmount:   transfer.Amount.Amount.Clone(),
  4983  		Asset:       transfer.Amount.Asset,
  4984  		Type:        types.TransferTypeReleaseHoldingAccount,
  4985  		FromAccount: []*types.Account{holdingAccount},
  4986  		ToAccount:   []*types.Account{targetAccount},
  4987  	}
  4988  
  4989  	res, err := e.getLedgerEntries(ctx, req)
  4990  	if err != nil {
  4991  		e.log.Error("Failed to transfer funds", logging.Error(err))
  4992  		return nil, err
  4993  	}
  4994  	for _, bal := range res.Balances {
  4995  		if err := e.IncrementBalance(ctx, bal.Account.ID, bal.Balance); err != nil {
  4996  			e.log.Error("Could not update the target account in transfer",
  4997  				logging.String("account-id", bal.Account.ID),
  4998  				logging.Error(err))
  4999  			return nil, err
  5000  		}
  5001  	}
  5002  	return res, nil
  5003  }
  5004  
  5005  // ClearSpotMarket moves remaining LP fees to the global reward account and removes market accounts.
  5006  func (e *Engine) ClearSpotMarket(ctx context.Context, mktID, quoteAsset string, parties []string) ([]*types.LedgerMovement, error) {
  5007  	resps := []*types.LedgerMovement{}
  5008  
  5009  	req := &types.TransferRequest{
  5010  		FromAccount: make([]*types.Account, 1),
  5011  		ToAccount:   make([]*types.Account, 1),
  5012  		Asset:       quoteAsset,
  5013  		Type:        types.TransferTypeClearAccount,
  5014  	}
  5015  
  5016  	for _, v := range parties {
  5017  		generalAcc, err := e.GetAccountByID(e.accountID(noMarket, v, quoteAsset, types.AccountTypeGeneral))
  5018  		if err != nil {
  5019  			e.log.Debug(
  5020  				"Failed to get the general account",
  5021  				logging.String("party-id", v),
  5022  				logging.String("market-id", mktID),
  5023  				logging.String("asset", quoteAsset),
  5024  				logging.Error(err))
  5025  			// just try to do other parties
  5026  			continue
  5027  		}
  5028  		// Then we do bond account
  5029  		bondAcc, err := e.GetAccountByID(e.accountID(mktID, v, quoteAsset, types.AccountTypeBond))
  5030  		if err != nil {
  5031  			// this not an actual error
  5032  			// a party may not have a bond account if
  5033  			// its not also a liquidity provider
  5034  			continue
  5035  		}
  5036  
  5037  		req.FromAccount[0] = bondAcc
  5038  		req.ToAccount[0] = generalAcc
  5039  		req.Amount = bondAcc.Balance.Clone()
  5040  
  5041  		if e.log.GetLevel() == logging.DebugLevel {
  5042  			e.log.Debug("Clearing party bond account",
  5043  				logging.String("market-id", mktID),
  5044  				logging.String("asset", quoteAsset),
  5045  				logging.String("party", v),
  5046  				logging.BigUint("bond-before", bondAcc.Balance),
  5047  				logging.BigUint("general-before", generalAcc.Balance),
  5048  				logging.BigUint("general-after", num.Sum(generalAcc.Balance, bondAcc.Balance)))
  5049  		}
  5050  
  5051  		ledgerEntries, err := e.clearAccount(ctx, req, v, quoteAsset, mktID)
  5052  		if err != nil {
  5053  			e.log.Panic("unable to clear party account", logging.Error(err))
  5054  		}
  5055  
  5056  		// add entries to the response
  5057  		resps = append(resps, ledgerEntries)
  5058  	}
  5059  
  5060  	treasury, _ := e.GetNetworkTreasuryAccount(quoteAsset)
  5061  	req = &types.TransferRequest{
  5062  		FromAccount: make([]*types.Account, 1),
  5063  		ToAccount:   make([]*types.Account, 1),
  5064  		Asset:       quoteAsset,
  5065  		Type:        types.TransferTypeClearAccount,
  5066  	}
  5067  	// any remaining balance in the fee account gets transferred over to the insurance account
  5068  	lpFeeAccID := e.accountID(mktID, "", quoteAsset, types.AccountTypeFeesLiquidity)
  5069  	if lpFeeAcc, ok := e.accs[lpFeeAccID]; ok {
  5070  		req.FromAccount[0] = lpFeeAcc
  5071  		req.ToAccount[0] = treasury
  5072  		req.Amount = lpFeeAcc.Balance.Clone()
  5073  		lpFeeLE, err := e.getLedgerEntries(ctx, req)
  5074  		if err != nil {
  5075  			e.log.Panic("unable to redistribute remainder of LP fee account funds", logging.Error(err))
  5076  		}
  5077  		resps = append(resps, lpFeeLE)
  5078  		for _, bal := range lpFeeLE.Balances {
  5079  			if err := e.IncrementBalance(ctx, bal.Account.ID, bal.Balance); err != nil {
  5080  				e.log.Error("Could not update the target account in transfer",
  5081  					logging.String("account-id", bal.Account.ID),
  5082  					logging.Error(err))
  5083  				return nil, err
  5084  			}
  5085  		}
  5086  		// remove the account once it's drained
  5087  		e.removeAccount(lpFeeAccID)
  5088  	}
  5089  
  5090  	makerFeeID := e.accountID(mktID, "", quoteAsset, types.AccountTypeFeesMaker)
  5091  	e.removeAccount(makerFeeID)
  5092  
  5093  	return resps, nil
  5094  }
  5095  
  5096  // CreateSpotMarketAccounts creates the required accounts for a market.
  5097  func (e *Engine) CreateSpotMarketAccounts(ctx context.Context, marketID, quoteAsset string) error {
  5098  	var err error
  5099  	if !e.AssetExists(quoteAsset) {
  5100  		return ErrInvalidAssetID
  5101  	}
  5102  
  5103  	// these are fee related account only
  5104  	liquidityFeeID := e.accountID(marketID, "", quoteAsset, types.AccountTypeFeesLiquidity)
  5105  	_, ok := e.accs[liquidityFeeID]
  5106  	if !ok {
  5107  		liquidityFeeAcc := &types.Account{
  5108  			ID:       liquidityFeeID,
  5109  			Asset:    quoteAsset,
  5110  			Owner:    systemOwner,
  5111  			Balance:  num.UintZero(),
  5112  			MarketID: marketID,
  5113  			Type:     types.AccountTypeFeesLiquidity,
  5114  		}
  5115  		e.accs[liquidityFeeID] = liquidityFeeAcc
  5116  		e.addAccountToHashableSlice(liquidityFeeAcc)
  5117  		e.broker.Send(events.NewAccountEvent(ctx, *liquidityFeeAcc))
  5118  	}
  5119  	makerFeeID := e.accountID(marketID, "", quoteAsset, types.AccountTypeFeesMaker)
  5120  	_, ok = e.accs[makerFeeID]
  5121  	if !ok {
  5122  		makerFeeAcc := &types.Account{
  5123  			ID:       makerFeeID,
  5124  			Asset:    quoteAsset,
  5125  			Owner:    systemOwner,
  5126  			Balance:  num.UintZero(),
  5127  			MarketID: marketID,
  5128  			Type:     types.AccountTypeFeesMaker,
  5129  		}
  5130  		e.accs[makerFeeID] = makerFeeAcc
  5131  		e.addAccountToHashableSlice(makerFeeAcc)
  5132  		e.broker.Send(events.NewAccountEvent(ctx, *makerFeeAcc))
  5133  	}
  5134  
  5135  	_, err = e.GetOrCreateLiquidityFeesBonusDistributionAccount(ctx, marketID, quoteAsset)
  5136  
  5137  	return err
  5138  }
  5139  
  5140  // PartyHasSufficientBalance checks if the party has sufficient amount in the <fromAccountType> account.
  5141  func (e *Engine) PartyHasSufficientBalance(asset, partyID string, amount *num.Uint, fromAccountType types.AccountType) error {
  5142  	party := partyID
  5143  	if party == types.NetworkParty {
  5144  		party = systemOwner
  5145  	}
  5146  	accId := e.accountID(noMarket, party, asset, fromAccountType)
  5147  	acc, err := e.GetAccountByID(accId)
  5148  	if err != nil {
  5149  		return err
  5150  	}
  5151  	if acc.Balance.LT(amount) {
  5152  		return ErrInsufficientFundsInAsset
  5153  	}
  5154  	return nil
  5155  }
  5156  
  5157  // CreatePartyHoldingAccount creates a holding account for a party.
  5158  func (e *Engine) CreatePartyHoldingAccount(ctx context.Context, partyID, asset string) (string, error) {
  5159  	if !e.AssetExists(asset) {
  5160  		return "", ErrInvalidAssetID
  5161  	}
  5162  
  5163  	holdingID := e.accountID(noMarket, partyID, asset, types.AccountTypeHolding)
  5164  	if _, ok := e.accs[holdingID]; !ok {
  5165  		acc := types.Account{
  5166  			ID:       holdingID,
  5167  			Asset:    asset,
  5168  			MarketID: noMarket,
  5169  			Balance:  num.UintZero(),
  5170  			Owner:    partyID,
  5171  			Type:     types.AccountTypeHolding,
  5172  		}
  5173  		e.accs[holdingID] = &acc
  5174  		e.addPartyAccount(partyID, holdingID, &acc)
  5175  		e.addAccountToHashableSlice(&acc)
  5176  		e.broker.Send(events.NewAccountEvent(ctx, acc))
  5177  	}
  5178  
  5179  	return holdingID, nil
  5180  }
  5181  
  5182  // TransferSpot transfers the given asset/quantity from partyID to partyID.
  5183  // The source partyID fromAccountType account must exist in the asset, the target partyID account in the asset is created if it doesn't yet exist.
  5184  func (e *Engine) TransferSpot(ctx context.Context, party, toParty, asset string, quantity *num.Uint, fromAccountType types.AccountType, toAccountType types.AccountType) (*types.LedgerMovement, error) {
  5185  	partyID := party
  5186  	if party == types.NetworkParty {
  5187  		partyID = systemOwner
  5188  	}
  5189  
  5190  	toPartyID := toParty
  5191  	if toParty == types.NetworkParty {
  5192  		toPartyID = systemOwner
  5193  	}
  5194  
  5195  	fromAccountID := e.accountID(noMarket, partyID, asset, fromAccountType)
  5196  	fromAccount, err := e.GetAccountByID(fromAccountID)
  5197  	if err != nil {
  5198  		return nil, err
  5199  	}
  5200  
  5201  	toAccountID := e.accountID(noMarket, toPartyID, asset, toAccountType)
  5202  	toAccount, err := e.GetAccountByID(toAccountID)
  5203  	if err != nil {
  5204  		if toAccountType == types.AccountTypeGeneral {
  5205  			toAccountID, _ = e.CreatePartyGeneralAccount(ctx, toPartyID, asset)
  5206  			toAccount, _ = e.GetAccountByID(toAccountID)
  5207  		} else if toPartyID == types.NetworkParty {
  5208  			toAccount = e.getOrCreateNetworkAccount(ctx, asset, toAccountType)
  5209  		}
  5210  	}
  5211  
  5212  	req := &types.TransferRequest{
  5213  		Amount:      quantity.Clone(),
  5214  		MinAmount:   quantity.Clone(),
  5215  		Asset:       asset,
  5216  		Type:        types.TransferTypeSpot,
  5217  		FromAccount: []*types.Account{fromAccount},
  5218  		ToAccount:   []*types.Account{toAccount},
  5219  	}
  5220  
  5221  	res, err := e.getLedgerEntries(ctx, req)
  5222  	if err != nil {
  5223  		e.log.Error("Failed to transfer funds", logging.Error(err))
  5224  		return nil, err
  5225  	}
  5226  	for _, bal := range res.Balances {
  5227  		if err := e.IncrementBalance(ctx, bal.Account.ID, bal.Balance); err != nil {
  5228  			e.log.Error("Could not update the target account in transfer",
  5229  				logging.String("account-id", bal.Account.ID),
  5230  				logging.Error(err))
  5231  			return nil, err
  5232  		}
  5233  	}
  5234  	return res, nil
  5235  }
  5236  
  5237  func (e *Engine) GetOrCreatePartyOrderMarginAccount(ctx context.Context, partyID, marketID, asset string) (string, error) {
  5238  	if !e.AssetExists(asset) {
  5239  		return "", ErrInvalidAssetID
  5240  	}
  5241  	marginID := e.accountID(marketID, partyID, asset, types.AccountTypeOrderMargin)
  5242  	if _, ok := e.accs[marginID]; !ok {
  5243  		acc := types.Account{
  5244  			ID:       marginID,
  5245  			Asset:    asset,
  5246  			MarketID: marketID,
  5247  			Balance:  num.UintZero(),
  5248  			Owner:    partyID,
  5249  			Type:     types.AccountTypeOrderMargin,
  5250  		}
  5251  		e.accs[marginID] = &acc
  5252  		e.addPartyAccount(partyID, marginID, &acc)
  5253  		e.addAccountToHashableSlice(&acc)
  5254  		e.broker.Send(events.NewAccountEvent(ctx, acc))
  5255  	}
  5256  	return marginID, nil
  5257  }
  5258  
  5259  func (e *Engine) GetVestingAccounts() []*types.Account {
  5260  	accs := []*types.Account{}
  5261  	for _, a := range e.accs {
  5262  		if a.Type == types.AccountTypeVestingRewards {
  5263  			accs = append(accs, a.Clone())
  5264  		}
  5265  	}
  5266  	sort.Slice(accs, func(i, j int) bool {
  5267  		return accs[i].ID < accs[j].ID
  5268  	})
  5269  	return accs
  5270  }
  5271  
  5272  func (e *Engine) EarmarkForAutomatedPurchase(asset string, accountType types.AccountType, min, max *num.Uint) (*num.Uint, error) {
  5273  	id := e.accountID(noMarket, systemOwner, asset, accountType)
  5274  	acc, err := e.GetAccountByID(id)
  5275  	if err != nil {
  5276  		return num.UintZero(), err
  5277  	}
  5278  	earmarked, ok := e.earmarkedBalance[id]
  5279  	if !ok {
  5280  		earmarked = num.UintZero()
  5281  	}
  5282  
  5283  	balanceToEarmark := acc.Balance.Clone()
  5284  	if earmarked.GT(balanceToEarmark) {
  5285  		e.log.Panic("earmarked balance is greater than account balance, this should never happen")
  5286  	}
  5287  	balanceToEarmark = balanceToEarmark.Sub(balanceToEarmark, earmarked)
  5288  
  5289  	if balanceToEarmark.IsZero() || balanceToEarmark.LT(min) {
  5290  		return num.UintZero(), fmt.Errorf("insufficient balance to earmark")
  5291  	}
  5292  	if balanceToEarmark.GT(max) {
  5293  		balanceToEarmark = num.Min(balanceToEarmark, max)
  5294  	}
  5295  
  5296  	earmarked.AddSum(balanceToEarmark)
  5297  	e.earmarkedBalance[id] = earmarked
  5298  	e.state.updateEarmarked(e.earmarkedBalance)
  5299  	return balanceToEarmark, nil
  5300  }
  5301  
  5302  func (e *Engine) UnearmarkForAutomatedPurchase(asset string, accountType types.AccountType, releaseRequest *num.Uint) error {
  5303  	id := e.accountID(noMarket, systemOwner, asset, accountType)
  5304  	_, err := e.GetAccountByID(id)
  5305  	if err != nil {
  5306  		return err
  5307  	}
  5308  	earmarked, ok := e.earmarkedBalance[id]
  5309  	if !ok || releaseRequest.GT(earmarked) {
  5310  		e.log.Panic("trying to unearmark an amount that is greater than the earmarked balance")
  5311  	}
  5312  	earmarked.Sub(earmarked, releaseRequest)
  5313  	if earmarked.IsZero() {
  5314  		delete(e.earmarkedBalance, id)
  5315  	}
  5316  	e.state.updateEarmarked(e.earmarkedBalance)
  5317  	return nil
  5318  }