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 }