import { injectable , singleton } from 'tsyringe' ; import { BlockchainEventWatcherEnum } from '../../../../models/enum' ; import { ContractService } from '../contractService/contractsService' ; import { WalletService } from '../walletService/walletService' ; import { SimpleStore } from '../../../libs/simpleStore/simpleStore' ; import { ArianeeEventEmitter , ArianeListenerEvent } from '../arianeeEventEmitterService/ArianeeEventEmitter' ; import { Web3Service } from '../web3Service/web3Service' ; import { blockchainEventsName } from '../../../../models/blockchainEventsName' ; import { WatchParameter } from '../../../../models/watchParameter' ; const blockchainEventCursorNamespaceKey = 'blockchainEventCursor' ; @ injectable ( ) export class BlockchainEventWatcherService { constructor ( private contractService , private walletService , public store , private eventEmitter , private web3Service ) { eventEmitter . EE . on ( ArianeListenerEvent . newListener , async ( event ) => { this . watcherParameters . filter ( conf => conf . eventNames . includes ( event ) ) . filter ( conf => ! this . onGoingWatchers . has ( this . createCompositeIdWatcher ( conf . eventNames ) ) ) . forEach ( async ( conf ) => { this . onGoingWatchers . add ( this . createCompositeIdWatcher ( conf . eventNames ) ) ; this . watch ( conf ) ; } ) ; } ) ; } private createCompositeIdWatcher = ( eventNames ) => { return eventNames . sort ( ) . join ( ) ; } ; private onGoingWatchers = new Set ( ) ; public timeout = 2000 ; public addWatchParameter = ( watcherParameter ) => { this . watcherParameters . push ( watcherParameter ) ; } ; public watcherParameters : WatchParameter [ ] = [ { contract : this . contractService . smartAssetContract , filter : { _from : this . walletService . address } , blockchainEvent : blockchainEventsName . smartAsset . transfer , eventNames : [ BlockchainEventWatcherEnum . Transfer , BlockchainEventWatcherEnum . TransferFrom ] } , { contract : this . contractService . smartAssetContract , filter : { _to : this . walletService . address } , blockchainEvent : blockchainEventsName . smartAsset . transfer , eventNames : [ BlockchainEventWatcherEnum . Transfer , BlockchainEventWatcherEnum . TransferTo ] } , { contract : this . contractService . identityContract , filter : { } , blockchainEvent : blockchainEventsName . identity . IdentityUpdate , eventNames : [ BlockchainEventWatcherEnum . IdentityUpdate ] } , { contract : this . contractService . identityContract , filter : { } , blockchainEvent : blockchainEventsName . identity . IdentityValidate , eventNames : [ BlockchainEventWatcherEnum . IdentityValidate ] } , { contract : this . contractService . messageContract , blockchainEvent : blockchainEventsName . message . MessageSent , filter : { _receiver : this . walletService . address } , eventNames : [ BlockchainEventWatcherEnum . MessageReceive ] } ] ; public watch = async ( conf ) => { setTimeout ( async ( ) => { const { contract , filter , blockchainEvent , eventNames } = conf ; const cursorKey = blockchainEvent . concat ( JSON . stringify ( filter ) ) + contract . options . address ; const currentBlock = await this . web3Service . web3 . eth . getBlockNumber ( ) ; const cursor = await this . store . get ( blockchainEventCursorNamespaceKey , cursorKey , ( ) => Promise . resolve ( currentBlock - 1 ) ) ; if ( currentBlock > cursor ) { const pastEvent = await contract . getPastEvents ( blockchainEvent , { fromBlock : cursor , toBlock : currentBlock , filter : filter } ) ; this . store . set ( blockchainEventCursorNamespaceKey , cursorKey , currentBlock + 1 ) ; eventNames . forEach ( ( eventName ) => { if ( pastEvent . length > 0 ) { this . eventEmitter . EE . emit ( eventName , pastEvent ) ; } } ) ; } const sumOfListeners = eventNames . reduce ( ( acc , eventName ) => { return acc + this . eventEmitter . EE . listeners ( eventName ) . length ; } , 0 ) ; if ( sumOfListeners > 0 ) { this . watch ( conf ) ; } else { this . onGoingWatchers . delete ( this . createCompositeIdWatcher ( conf . eventNames ) ) ; } } , this . timeout ) ; } ; }