code.vegaprotocol.io/vega@v0.79.0/core/banking/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 banking 17 18 import ( 19 "context" 20 "errors" 21 "fmt" 22 "math/big" 23 "slices" 24 "sort" 25 "strings" 26 "sync/atomic" 27 "time" 28 29 "code.vegaprotocol.io/vega/core/assets" 30 "code.vegaprotocol.io/vega/core/broker" 31 "code.vegaprotocol.io/vega/core/events" 32 "code.vegaprotocol.io/vega/core/types" 33 "code.vegaprotocol.io/vega/core/validators" 34 "code.vegaprotocol.io/vega/libs/num" 35 "code.vegaprotocol.io/vega/logging" 36 "code.vegaprotocol.io/vega/protos/vega" 37 proto "code.vegaprotocol.io/vega/protos/vega" 38 39 "github.com/emirpasic/gods/sets/treeset" 40 "golang.org/x/exp/maps" 41 ) 42 43 //go:generate go run github.com/golang/mock/mockgen -destination mocks/mocks.go -package mocks code.vegaprotocol.io/vega/core/banking Assets,Notary,Collateral,Witness,TimeService,EpochService,Topology,MarketActivityTracker,ERC20BridgeView,EthereumEventSource,Parties,StakeAccounting 44 45 var ( 46 ErrWrongAssetTypeUsedInBuiltinAssetChainEvent = errors.New("non builtin asset used for builtin asset chain event") 47 ErrWrongAssetTypeUsedInERC20ChainEvent = errors.New("non ERC20 for ERC20 chain event") 48 ErrWrongAssetUsedForERC20Withdraw = errors.New("non erc20 asset used for lock withdraw") 49 ErrInvalidWithdrawalState = errors.New("invalid withdrawal state") 50 ErrNotMatchingWithdrawalForReference = errors.New("invalid reference for withdrawal chain event") 51 ErrWithdrawalNotReady = errors.New("withdrawal not ready") 52 ErrNotEnoughFundsToTransfer = errors.New("not enough funds to transfer") 53 ) 54 55 type Assets interface { 56 Get(assetID string) (*assets.Asset, error) 57 Enable(ctx context.Context, assetID string) error 58 ApplyAssetUpdate(ctx context.Context, assetID string) error 59 } 60 61 // Notary ... 62 63 type Notary interface { 64 StartAggregate(resID string, kind types.NodeSignatureKind, signature []byte) 65 IsSigned(ctx context.Context, id string, kind types.NodeSignatureKind) ([]types.NodeSignature, bool) 66 OfferSignatures(kind types.NodeSignatureKind, f func(resources string) []byte) 67 } 68 69 // Collateral engine. 70 type Collateral interface { 71 Deposit(ctx context.Context, party, asset string, amount *num.Uint) (*types.LedgerMovement, error) 72 Withdraw(ctx context.Context, party, asset string, amount *num.Uint) (*types.LedgerMovement, error) 73 EnableAsset(ctx context.Context, asset types.Asset) error 74 GetPartyGeneralAccount(party, asset string) (*types.Account, error) 75 GetPartyLockedForStaking(party, asset string) (*types.Account, error) 76 GetPartyVestedRewardAccount(partyID, asset string) (*types.Account, error) 77 TransferFunds(ctx context.Context, 78 transfers []*types.Transfer, 79 accountTypes []types.AccountType, 80 references []string, 81 feeTransfers []*types.Transfer, 82 feeTransfersAccountTypes []types.AccountType, 83 ) ([]*types.LedgerMovement, error) 84 GovernanceTransferFunds(ctx context.Context, transfers []*types.Transfer, accountTypes []types.AccountType, references []string) ([]*types.LedgerMovement, error) 85 PropagateAssetUpdate(ctx context.Context, asset types.Asset) error 86 GetSystemAccountBalance(asset, market string, accountType types.AccountType) (*num.Uint, error) 87 } 88 89 // Witness provide foreign chain resources validations. 90 type Witness interface { 91 StartCheck(validators.Resource, func(interface{}, bool), time.Time) error 92 RestoreResource(validators.Resource, func(interface{}, bool)) error 93 } 94 95 // TimeService provide the time of the vega node using the tm time. 96 type TimeService interface { 97 GetTimeNow() time.Time 98 } 99 100 // Epochervice ... 101 type EpochService interface { 102 NotifyOnEpoch(f func(context.Context, types.Epoch), r func(context.Context, types.Epoch)) 103 } 104 105 // Topology ... 106 type Topology interface { 107 IsValidator() bool 108 } 109 110 // StakeAccounting ... 111 type StakeAccounting interface { 112 AddEvent(ctx context.Context, evt *types.StakeLinking) 113 } 114 115 type MarketActivityTracker interface { 116 CalculateMetricForIndividuals(ctx context.Context, ds *vega.DispatchStrategy) []*types.PartyContributionScore 117 CalculateMetricForTeams(ctx context.Context, ds *vega.DispatchStrategy) ([]*types.PartyContributionScore, map[string][]*types.PartyContributionScore) 118 GetMarketsWithEligibleProposer(asset string, markets []string, payoutAsset string, funder string, eligibleKeys []string) []*types.MarketContributionScore 119 MarkPaidProposer(asset, market, payoutAsset string, marketsInScope []string, funder string) 120 MarketTrackedForAsset(market, asset string) bool 121 TeamStatsForMarkets(allMarketsForAssets, onlyTheseMarkets []string) map[string]map[string]*num.Uint 122 PublishGameMetric(ctx context.Context, dispatchStrategy []*vega.DispatchStrategy, now time.Time) 123 GameFinished(gameID string) 124 GetNotionalVolumeForAsset(asset string, markets []string, windowSize int) *num.Uint 125 } 126 127 type EthereumEventSource interface { 128 UpdateContractBlock(string, string, uint64) 129 } 130 131 type Parties interface { 132 CheckDerivedKeyOwnership(party types.PartyID, derivedKey string) bool 133 } 134 135 const ( 136 pendingState uint32 = iota 137 okState 138 rejectedState 139 ) 140 141 var defaultValidationDuration = 30 * 24 * time.Hour 142 143 type dispatchStrategyCacheEntry struct { 144 ds *proto.DispatchStrategy 145 refCount int 146 } 147 148 type Engine struct { 149 cfg Config 150 log *logging.Logger 151 timeService TimeService 152 broker broker.Interface 153 col Collateral 154 witness Witness 155 notary Notary 156 assets Assets 157 top Topology 158 parties Parties 159 160 // assetActions tracks all the asset actions the engine must process on network 161 // tick. 162 assetActions map[string]*assetAction 163 // seenAssetActions keeps track of all asset action the node has seen. 164 seenAssetActions *treeset.Set 165 166 // primaryEthChainID stores the Ethereum Mainnet chain ID. It is used during the 167 // chain event deduplication phase, to ensure we correctly deduplicate 168 // chain events that have been seen before the introduce of the second bridge. 169 primaryEthChainID string 170 // lastSeenPrimaryEthBlock holds the block height of the latest ERC20 chain 171 // event, from the primary chain, processed by the engine. 172 lastSeenPrimaryEthBlock uint64 173 primaryBridgeState *bridgeState 174 primaryBridgeView ERC20BridgeView 175 176 // lastSeenSecondaryEthBlock holds the block height of the latest ERC20 chain 177 // event, from the secondary chain, processed by the engine. 178 lastSeenSecondaryEthBlock uint64 179 secondaryEthChainID string 180 secondaryBridgeState *bridgeState 181 secondaryBridgeView ERC20BridgeView 182 183 // map from chain-id -> collateral contract address 184 bridgeAddresses map[string]string 185 186 ethEventSource EthereumEventSource 187 188 withdrawals map[string]withdrawalRef 189 withdrawalCnt *big.Int 190 deposits map[string]*types.Deposit 191 192 currentEpoch uint64 193 bss *bankingSnapshotState 194 195 marketActivityTracker MarketActivityTracker 196 197 // transfer fee related stuff 198 scheduledTransfers map[int64][]scheduledTransfer 199 transferFeeFactor num.Decimal 200 minTransferQuantumMultiple num.Decimal 201 maxQuantumAmount num.Decimal 202 203 feeDiscountDecayFraction num.Decimal 204 feeDiscountMinimumTrackedAmount num.Decimal 205 206 // assetID -> partyID -> fee discount 207 pendingPerAssetAndPartyFeeDiscountUpdates map[string]map[string]*num.Uint 208 feeDiscountPerPartyAndAsset map[partyAssetKey]*num.Uint 209 210 scheduledGovernanceTransfers map[int64][]*types.GovernanceTransfer 211 recurringGovernanceTransfers []*types.GovernanceTransfer 212 recurringGovernanceTransfersMap map[string]*types.GovernanceTransfer 213 214 // a hash of a dispatch strategy to the dispatch strategy details 215 hashToStrategy map[string]*dispatchStrategyCacheEntry 216 217 // recurring transfers in the order they were created 218 recurringTransfers []*types.RecurringTransfer 219 // transfer id to recurringTransfers 220 recurringTransfersMap map[string]*types.RecurringTransfer 221 222 minWithdrawQuantumMultiple num.Decimal 223 224 maxGovTransferQunatumMultiplier num.Decimal 225 maxGovTransferFraction num.Decimal 226 227 metricUpdateFrequency time.Duration 228 nextMetricUpdate time.Time 229 230 // transient cache used to market a dispatch strategy as checked for eligibility for this round so we don't check again. 231 dispatchRequiredCache map[string]bool 232 233 stakingAsset string 234 stakeAccounting StakeAccounting 235 } 236 237 type withdrawalRef struct { 238 w *types.Withdrawal 239 ref *big.Int 240 } 241 242 func New(log *logging.Logger, 243 cfg Config, 244 col Collateral, 245 witness Witness, 246 tsvc TimeService, 247 assets Assets, 248 notary Notary, 249 broker broker.Interface, 250 top Topology, 251 marketActivityTracker MarketActivityTracker, 252 primaryBridgeView ERC20BridgeView, 253 secondaryBridgeView ERC20BridgeView, 254 ethEventSource EthereumEventSource, 255 parties Parties, 256 stakeAccounting StakeAccounting, 257 ) (e *Engine) { 258 log = log.Named(namedLogger) 259 log.SetLevel(cfg.Level.Get()) 260 261 return &Engine{ 262 cfg: cfg, 263 log: log, 264 timeService: tsvc, 265 broker: broker, 266 col: col, 267 witness: witness, 268 assets: assets, 269 notary: notary, 270 top: top, 271 ethEventSource: ethEventSource, 272 parties: parties, 273 assetActions: map[string]*assetAction{}, 274 seenAssetActions: treeset.NewWithStringComparator(), 275 withdrawals: map[string]withdrawalRef{}, 276 deposits: map[string]*types.Deposit{}, 277 withdrawalCnt: big.NewInt(0), 278 bss: &bankingSnapshotState{}, 279 scheduledTransfers: map[int64][]scheduledTransfer{}, 280 recurringTransfers: []*types.RecurringTransfer{}, 281 recurringTransfersMap: map[string]*types.RecurringTransfer{}, 282 scheduledGovernanceTransfers: map[int64][]*types.GovernanceTransfer{}, 283 recurringGovernanceTransfers: []*types.GovernanceTransfer{}, 284 recurringGovernanceTransfersMap: map[string]*types.GovernanceTransfer{}, 285 transferFeeFactor: num.DecimalZero(), 286 minTransferQuantumMultiple: num.DecimalZero(), 287 minWithdrawQuantumMultiple: num.DecimalZero(), 288 marketActivityTracker: marketActivityTracker, 289 nextMetricUpdate: time.Time{}, 290 hashToStrategy: map[string]*dispatchStrategyCacheEntry{}, 291 primaryBridgeState: &bridgeState{ 292 active: true, 293 }, 294 secondaryBridgeState: &bridgeState{ 295 active: true, 296 }, 297 bridgeAddresses: map[string]string{}, 298 feeDiscountPerPartyAndAsset: map[partyAssetKey]*num.Uint{}, 299 pendingPerAssetAndPartyFeeDiscountUpdates: map[string]map[string]*num.Uint{}, 300 primaryBridgeView: primaryBridgeView, 301 secondaryBridgeView: secondaryBridgeView, 302 dispatchRequiredCache: map[string]bool{}, 303 stakeAccounting: stakeAccounting, 304 } 305 } 306 307 func (e *Engine) OnStakingAsset(_ context.Context, a string) error { 308 e.stakingAsset = a 309 return nil 310 } 311 312 func (e *Engine) OnMaxFractionChanged(ctx context.Context, f num.Decimal) error { 313 e.maxGovTransferFraction = f 314 return nil 315 } 316 317 func (e *Engine) OnMaxAmountChanged(ctx context.Context, f num.Decimal) error { 318 e.maxGovTransferQunatumMultiplier = f 319 return nil 320 } 321 322 func (e *Engine) OnMinWithdrawQuantumMultiple(ctx context.Context, f num.Decimal) error { 323 e.minWithdrawQuantumMultiple = f 324 return nil 325 } 326 327 func (e *Engine) OnPrimaryEthChainIDUpdated(chainID, collateralAddress string) { 328 e.primaryEthChainID = chainID 329 e.bridgeAddresses[chainID] = collateralAddress 330 } 331 332 func (e *Engine) OnSecondaryEthChainIDUpdated(chainID, collateralAddress string) { 333 e.secondaryEthChainID = chainID 334 e.bridgeAddresses[chainID] = collateralAddress 335 } 336 337 // ReloadConf updates the internal configuration. 338 func (e *Engine) ReloadConf(cfg Config) { 339 e.log.Info("reloading configuration") 340 if e.log.GetLevel() != cfg.Level.Get() { 341 e.log.Info("updating log level", 342 logging.String("old", e.log.GetLevel().String()), 343 logging.String("new", cfg.Level.String()), 344 ) 345 e.log.SetLevel(cfg.Level.Get()) 346 } 347 348 e.cfg = cfg 349 } 350 351 func (e *Engine) OnBlockEnd(ctx context.Context, now time.Time) { 352 if !now.Before(e.nextMetricUpdate) { 353 e.publishMetricData(ctx, now) 354 e.nextMetricUpdate = now.Add(e.metricUpdateFrequency) 355 } 356 } 357 358 // publishMetricData requests the market activity tracker to publish and event 359 // for each game with the current metric data for each party. 360 func (e *Engine) publishMetricData(ctx context.Context, now time.Time) { 361 hashes := make([]string, 0, len(e.hashToStrategy)) 362 for hash := range e.hashToStrategy { 363 hashes = append(hashes, hash) 364 } 365 sort.Strings(hashes) 366 dss := make([]*vega.DispatchStrategy, 0, len(hashes)) 367 for _, hash := range hashes { 368 dss = append(dss, e.hashToStrategy[hash].ds) 369 } 370 e.marketActivityTracker.PublishGameMetric(ctx, dss, now) 371 } 372 373 func (e *Engine) OnEpoch(ctx context.Context, ep types.Epoch) { 374 switch ep.Action { 375 case proto.EpochAction_EPOCH_ACTION_START: 376 e.currentEpoch = ep.Seq 377 e.cleanupStaleDispatchStrategies() 378 case proto.EpochAction_EPOCH_ACTION_END: 379 e.distributeRecurringTransfers(ctx, e.currentEpoch) 380 e.distributeRecurringGovernanceTransfers(ctx) 381 e.applyPendingFeeDiscountsUpdates(ctx) 382 e.sendTeamsStats(ctx, ep.Seq) 383 e.dispatchRequiredCache = map[string]bool{} 384 // as the metrics are going to be published here, we want to progress the next update. 385 e.nextMetricUpdate = e.timeService.GetTimeNow().Add(e.metricUpdateFrequency) 386 default: 387 e.log.Panic("epoch action should never be UNSPECIFIED", logging.String("epoch", ep.String())) 388 } 389 } 390 391 func (e *Engine) OnTick(ctx context.Context, now time.Time) { 392 e.processAssetActions(ctx, now) 393 394 e.notary.OfferSignatures(types.NodeSignatureKindAssetWithdrawal, e.offerERC20NotarySignatures) 395 396 // then process all scheduledTransfers 397 if err := e.distributeScheduledTransfers(ctx, now); err != nil { 398 e.log.Error("could not process scheduled transfers", 399 logging.Error(err), 400 ) 401 } 402 403 // process governance transfers 404 e.distributeScheduledGovernanceTransfers(ctx, now) 405 } 406 407 func (e *Engine) processAssetActions(ctx context.Context, now time.Time) { 408 sortedAssetActionKeys := maps.Keys(e.assetActions) 409 sort.Strings(sortedAssetActionKeys) 410 411 for _, key := range sortedAssetActionKeys { 412 action := e.assetActions[key] 413 414 switch action.state.Load() { 415 case pendingState: 416 // The verification of the action has not been completed yet, so 417 // we skip it until it is. 418 continue 419 case okState: 420 if err := e.deduplicateAssetAction(ctx, action); err != nil { 421 e.log.Warn("an error occurred during asset action deduplication", 422 logging.Error(err), 423 logging.String("action", action.String()), 424 logging.String("tx-hash", action.txHash), 425 logging.String("chain-id", action.chainID)) 426 break 427 } 428 429 if err := e.finalizeAction(ctx, action, now); err != nil { 430 e.log.Error("unable to finalize action", 431 logging.String("action", action.String()), 432 logging.Error(err)) 433 } 434 case rejectedState: 435 e.log.Error("network rejected banking action", 436 logging.String("action", action.String())) 437 } 438 439 delete(e.assetActions, key) 440 } 441 } 442 443 func (e *Engine) onCheckDone(i interface{}, valid bool) { 444 aa, ok := i.(*assetAction) 445 if !ok { 446 return 447 } 448 449 newState := rejectedState 450 if valid { 451 newState = okState 452 } 453 aa.state.Store(newState) 454 } 455 456 func (e *Engine) getWithdrawalFromRef(ref *big.Int) (*types.Withdrawal, error) { 457 // sort withdraws to check deterministically 458 withdrawalsK := make([]string, 0, len(e.withdrawals)) 459 for k := range e.withdrawals { 460 withdrawalsK = append(withdrawalsK, k) 461 } 462 sort.Strings(withdrawalsK) 463 464 for _, k := range withdrawalsK { 465 v := e.withdrawals[k] 466 if v.ref.Cmp(ref) == 0 { 467 return v.w, nil 468 } 469 } 470 471 return nil, ErrNotMatchingWithdrawalForReference 472 } 473 474 func (e *Engine) dedupAction(ctx context.Context, aa *assetAction) error { 475 switch { 476 case aa.IsBuiltinAssetDeposit(): 477 dep := e.deposits[aa.id] 478 return e.dedupDeposit(ctx, dep) 479 case aa.IsERC20Deposit(): 480 dep := e.deposits[aa.id] 481 return e.dedupDeposit(ctx, dep) 482 } 483 // the bridge stop/resume actions don't send events, and the asset listing/updates share the same 484 // underlying asset ID, so they don't result in duplicates. Only deposits need to be handled. 485 e.log.Warn("unable to deduplicate asset action", 486 logging.String("action", aa.String())) 487 return nil 488 } 489 490 func (e *Engine) finalizeAction(ctx context.Context, aa *assetAction, now time.Time) error { 491 // tell the evt forwarder tracker about this block height 492 if addr, ok := e.bridgeAddresses[aa.chainID]; ok { 493 e.ethEventSource.UpdateContractBlock(addr, aa.chainID, aa.blockHeight) 494 } 495 496 switch { 497 case aa.IsBuiltinAssetDeposit(): 498 dep := e.deposits[aa.id] 499 return e.finalizeDeposit(ctx, dep, now) 500 case aa.IsERC20Deposit(): 501 dep := e.deposits[aa.id] 502 return e.finalizeDeposit(ctx, dep, now) 503 case aa.IsERC20AssetList(): 504 return e.finalizeAssetList(ctx, aa.erc20AL.VegaAssetID) 505 case aa.IsERC20AssetLimitsUpdated(): 506 return e.finalizeAssetLimitsUpdated(ctx, aa.erc20AssetLimitsUpdated.VegaAssetID) 507 case aa.IsERC20BridgeStopped(): 508 b, err := e.bridgeStateForChainID(aa.chainID) 509 if err != nil { 510 return err 511 } 512 b.NewBridgeStopped(aa.blockHeight, aa.logIndex) 513 return nil 514 case aa.IsERC20BridgeResumed(): 515 b, err := e.bridgeStateForChainID(aa.chainID) 516 if err != nil { 517 return err 518 } 519 b.NewBridgeResumed(aa.blockHeight, aa.logIndex) 520 return nil 521 default: 522 return ErrUnknownAssetAction 523 } 524 } 525 526 func (e *Engine) finalizeAssetList(ctx context.Context, assetID string) error { 527 asset, err := e.assets.Get(assetID) 528 if err != nil { 529 e.log.Error("invalid asset id used to finalise asset list", 530 logging.Error(err), 531 logging.AssetID(assetID)) 532 return nil 533 } 534 if err := e.assets.Enable(ctx, assetID); err != nil { 535 e.log.Error("unable to enable asset", 536 logging.Error(err), 537 logging.AssetID(assetID)) 538 return err 539 } 540 return e.col.EnableAsset(ctx, *asset.ToAssetType()) 541 } 542 543 func (e *Engine) finalizeAssetLimitsUpdated(ctx context.Context, assetID string) error { 544 asset, err := e.assets.Get(assetID) 545 if err != nil { 546 e.log.Error("invalid asset id used to finalise asset list", 547 logging.Error(err), 548 logging.AssetID(assetID)) 549 return nil 550 } 551 if err := e.assets.ApplyAssetUpdate(ctx, assetID); err != nil { 552 e.log.Error("couldn't apply asset update", 553 logging.Error(err), 554 logging.AssetID(assetID)) 555 return err 556 } 557 return e.col.PropagateAssetUpdate(ctx, *asset.ToAssetType()) 558 } 559 560 func (e *Engine) finalizeDeposit(ctx context.Context, d *types.Deposit, now time.Time) error { 561 defer func() { 562 e.broker.Send(events.NewDepositEvent(ctx, *d)) 563 // whatever happens, the deposit is in its final state (cancelled or finalized) 564 delete(e.deposits, d.ID) 565 }() 566 res, err := e.col.Deposit(ctx, d.PartyID, d.Asset, d.Amount) 567 if err != nil { 568 d.Status = types.DepositStatusCancelled 569 return err 570 } 571 572 d.Status = types.DepositStatusFinalized 573 d.CreditDate = now.UnixNano() 574 e.broker.Send(events.NewLedgerMovements(ctx, []*types.LedgerMovement{res})) 575 return nil 576 } 577 578 func (e *Engine) dedupDeposit(ctx context.Context, d *types.Deposit) error { 579 d.Status = types.DepositStatusDuplicateRejected 580 e.broker.Send(events.NewDepositEvent(ctx, *d)) 581 return nil 582 } 583 584 func (e *Engine) finalizeWithdraw(ctx context.Context, w *types.Withdrawal) error { 585 // always send the withdrawal event, don't delete it from the map because we 586 // may still receive events 587 defer func() { 588 e.broker.Send(events.NewWithdrawalEvent(ctx, *w)) 589 }() 590 591 res, err := e.col.Withdraw(ctx, w.PartyID, w.Asset, w.Amount.Clone()) 592 if err != nil { 593 w.Status = types.WithdrawalStatusRejected 594 return err 595 } 596 597 w.Status = types.WithdrawalStatusFinalized 598 e.broker.Send(events.NewLedgerMovements(ctx, []*types.LedgerMovement{res})) 599 return nil 600 } 601 602 func (e *Engine) newWithdrawal( 603 id, partyID, asset string, 604 amount *num.Uint, 605 wext *types.WithdrawExt, 606 ) (w *types.Withdrawal, ref *big.Int) { 607 partyID = strings.TrimPrefix(partyID, "0x") 608 asset = strings.TrimPrefix(asset, "0x") 609 now := e.timeService.GetTimeNow() 610 611 // reference needs to be an int, deterministic for the contracts 612 ref = big.NewInt(0).Add(e.withdrawalCnt, big.NewInt(now.Unix())) 613 e.withdrawalCnt.Add(e.withdrawalCnt, big.NewInt(1)) 614 w = &types.Withdrawal{ 615 ID: id, 616 Status: types.WithdrawalStatusOpen, 617 PartyID: partyID, 618 Asset: asset, 619 Amount: amount, 620 Ext: wext, 621 CreationDate: now.UnixNano(), 622 Ref: ref.String(), 623 } 624 return 625 } 626 627 func (e *Engine) newDeposit( 628 id, partyID, asset string, 629 amount *num.Uint, 630 txHash string, 631 ) *types.Deposit { 632 partyID = strings.TrimPrefix(partyID, "0x") 633 asset = strings.TrimPrefix(asset, "0x") 634 return &types.Deposit{ 635 ID: id, 636 Status: types.DepositStatusOpen, 637 PartyID: partyID, 638 Asset: asset, 639 Amount: amount, 640 CreationDate: e.timeService.GetTimeNow().UnixNano(), 641 TxHash: txHash, 642 } 643 } 644 645 func (e *Engine) GetDispatchStrategy(hash string) *proto.DispatchStrategy { 646 ds, ok := e.hashToStrategy[hash] 647 if !ok { 648 e.log.Warn("could not find dispatch strategy in banking engine", logging.String("hash", hash)) 649 return nil 650 } 651 652 if ds.refCount == 0 { 653 return nil 654 } 655 656 return ds.ds 657 } 658 659 // sendTeamsStats sends the teams statistics, which only account for games. 660 // This is located here not because this is where it should be, but because 661 // we don't know where to put it, as we need to have access to the dispatch 662 // strategy. 663 func (e *Engine) sendTeamsStats(ctx context.Context, seq uint64) { 664 onlyTheseMarkets := map[string]interface{}{} 665 allMarketsForAssets := map[string]interface{}{} 666 for _, ds := range e.hashToStrategy { 667 if ds.ds.EntityScope == proto.EntityScope_ENTITY_SCOPE_TEAMS { 668 if len(ds.ds.Markets) != 0 { 669 // If there is no markets specified, then we need gather data from 670 // all markets tied to this asset. 671 allMarketsForAssets[ds.ds.AssetForMetric] = nil 672 } else { 673 for _, market := range ds.ds.Markets { 674 onlyTheseMarkets[market] = nil 675 } 676 } 677 } 678 } 679 680 if len(allMarketsForAssets) == 0 && len(onlyTheseMarkets) == 0 { 681 return 682 } 683 684 allMarketsForAssetsS := maps.Keys(allMarketsForAssets) 685 slices.Sort(allMarketsForAssetsS) 686 onlyTheseMarketsS := maps.Keys(onlyTheseMarkets) 687 slices.Sort(onlyTheseMarketsS) 688 689 teamsStats := e.marketActivityTracker.TeamStatsForMarkets(allMarketsForAssetsS, onlyTheseMarketsS) 690 691 if len(teamsStats) > 0 { 692 e.broker.Send(events.NewTeamsStatsUpdatedEvent(ctx, seq, teamsStats)) 693 } 694 } 695 696 func (e *Engine) bridgeViewForChainID(chainID string) (ERC20BridgeView, error) { 697 switch chainID { 698 case e.primaryEthChainID: 699 return e.primaryBridgeView, nil 700 case e.secondaryEthChainID: 701 return e.secondaryBridgeView, nil 702 default: 703 return nil, fmt.Errorf("chain id %q is not supported", chainID) 704 } 705 } 706 707 func (e *Engine) bridgeStateForChainID(chainID string) (*bridgeState, error) { 708 switch chainID { 709 case e.primaryEthChainID: 710 return e.primaryBridgeState, nil 711 case e.secondaryEthChainID: 712 return e.secondaryBridgeState, nil 713 default: 714 return nil, fmt.Errorf("chain id %q is not supported", chainID) 715 } 716 } 717 718 func newPendingState() *atomic.Uint32 { 719 state := &atomic.Uint32{} 720 state.Store(pendingState) 721 return state 722 }