import { Injectable } from '@angular/core' ; import { BehaviorSubject , combineLatest , Observable } from 'rxjs' ; import { SwapFormService } from 'src/app/features/swaps/services/swaps-form-service/swap-form.service' ; import { BridgeTokenPairsByBlockchains } from '@features/bridge/models/bridge-token-pairs-by-blockchains' ; import { filter , first , pairwise , startWith } from 'rxjs/operators' ; import { BridgeService } from 'src/app/features/bridge/services/bridge-service/bridge.service' ; import { TokensService } from 'src/app/core/services/tokens/tokens.service' ; import BigNumber from 'bignumber.js' ; import { BLOCKCHAIN_NAME } from '@shared/models/blockchain/blockchain-name' ; import { BridgeToken , BridgeTokenPair } from '@features/bridge/models/bridge-token-pair' ; import { TokenAmount } from '@shared/models/tokens/token-amount' ; import { List } from 'immutable' ; import { IframeService } from 'src/app/core/services/iframe/iframe.service' ; import { SwapFormInput } from '@features/swaps/models/swap-form' ; import { compareAddresses , compareTokens } from 'src/app/shared/utils/utils' ; import { SWAP_PROVIDER_TYPE } from 'src/app/features/swaps/models/swap-provider-type' ; @ Injectable ( ) export class SwapsService { private _swapProviderType$ = new BehaviorSubject < SWAP_PROVIDER_TYPE > ( undefined ) ; private _availableTokens$ = new BehaviorSubject < List < TokenAmount > > ( undefined ) ; private _availableFavoriteTokens$ = new BehaviorSubject < List < TokenAmount > > ( undefined ) ; private _bridgeTokenPairsByBlockchainsArray$ = new BehaviorSubject < List < BridgeTokenPairsByBlockchains > > ( undefined ) ; private _bridgeTokenPairsByBlockchainsFavoriteArray$ = new BehaviorSubject < List < BridgeTokenPairsByBlockchains > > ( undefined ) ; private intervalId : NodeJS . Timeout ; get availableTokens$ ( ) : Observable < List < TokenAmount > > { return this . _availableTokens$ . asObservable ( ) ; } get availableFavoriteTokens$ ( ) : Observable < List < TokenAmount > > { return this . _availableFavoriteTokens$ . asObservable ( ) ; } get bridgeTokenPairsByBlockchainsArray$ ( ) : Observable < List < BridgeTokenPairsByBlockchains > > { return this . _bridgeTokenPairsByBlockchainsArray$ . asObservable ( ) ; } get bridgeTokenPairsByBlockchainsFavoriteArray$ ( ) : Observable < List < BridgeTokenPairsByBlockchains > > { return this . _bridgeTokenPairsByBlockchainsFavoriteArray$ . asObservable ( ) ; } get swapMode$ ( ) : Observable < SWAP_PROVIDER_TYPE | null > { return this . _swapProviderType$ . asObservable ( ) ; } get swapMode ( ) : SWAP_PROVIDER_TYPE | null { return this . _swapProviderType$ . getValue ( ) ; } set swapMode ( swapType ) { this . _swapProviderType$ . next ( swapType ) ; } constructor ( private readonly swapFormService , private readonly bridgeService , private readonly tokensService , private readonly iframeService ) { this . subscribeOnTokens ( ) ; this . subscribeOnForm ( ) ; } private subscribeOnTokens ( ) { combineLatest ( [ this . bridgeService . tokens$ . pipe ( filter ( tokens => ! ! tokens . length ) ) , this . tokensService . tokens$ . pipe ( filter ( tokens => ! ! tokens . size ) ) , this . tokensService . favoriteTokens$ ] ) . subscribe ( ( [ bridgeTokenPairsByBlockchainsArray , tokenAmounts , favoriteTokenAmounts ] ) => { const updatedTokenAmounts = tokenAmounts . toArray ( ) ; const updatedFavoriteTokenAmounts = favoriteTokenAmounts . toArray ( ) ; const updatedBridgeTokenPairsByBlockchainsArray = this . getBridgePairsArrayAndModifyTokensAmount ( bridgeTokenPairsByBlockchainsArray , tokenAmounts , updatedTokenAmounts ) ; const updatedBridgeFavoriteTokenPairsByBlockchainsArray = this . getBridgePairsArrayAndModifyTokensAmount ( bridgeTokenPairsByBlockchainsArray , favoriteTokenAmounts , updatedFavoriteTokenAmounts ) ; updatedBridgeTokenPairsByBlockchainsArray . forEach ( item => ( item . tokenPairs = item . tokenPairs . filter ( pair => Object . values ( pair . tokenByBlockchain ) . every ( bridgeToken => bridgeToken ) ) ) ) ; updatedBridgeFavoriteTokenPairsByBlockchainsArray . forEach ( item => ( item . tokenPairs = item . tokenPairs . filter ( pair => Object . values ( pair . tokenByBlockchain ) . every ( bridgeToken => bridgeToken ) ) ) ) ; this . _bridgeTokenPairsByBlockchainsArray$ . next ( List ( updatedBridgeTokenPairsByBlockchainsArray ) ) ; this . _bridgeTokenPairsByBlockchainsFavoriteArray$ . next ( List ( updatedBridgeFavoriteTokenPairsByBlockchainsArray ) ) ; this . _availableTokens$ . next ( List ( updatedTokenAmounts ) ) ; const availableFavoriteTokens = List ( updatedFavoriteTokenAmounts . filter ( tokenA => favoriteTokenAmounts . some ( tokenB => compareTokens ( tokenA , tokenB ) ) ) ) ; this . _availableFavoriteTokens$ . next ( availableFavoriteTokens ) ; } ) ; } param pairsArray param tokenAmounts param updatedTokenAmounts private private getBridgePairsArrayAndModifyTokensAmount ( pairsArray , tokenAmounts , updatedTokenAmounts ) { return pairsArray . map ( tokenPairsByBlockchains => { const { fromBlockchain , toBlockchain } = tokenPairsByBlockchains ; return { ... tokenPairsByBlockchains , tokenPairs : tokenPairsByBlockchains . tokenPairs . map ( tokenPair => ( { ... tokenPair , tokenByBlockchain : { [ fromBlockchain ] : this . getUpdatedBridgeToken ( fromBlockchain , tokenPair , tokenAmounts , updatedTokenAmounts ) , [ toBlockchain ] : this . getUpdatedBridgeToken ( toBlockchain , tokenPair , tokenAmounts , updatedTokenAmounts ) } } ) ) } ; } ) ; } private subscribeOnForm ( ) { this . swapFormService . inputValueChanges . pipe ( startWith ( null , this . swapFormService . inputValue ) , pairwise ( ) ) . subscribe ( ( [ prevForm , curForm ] ) => { this . setSwapProviderType ( curForm ) ; if ( ( ! TokensService . areTokensEqual ( prevForm ?. fromToken , curForm . fromToken ) && curForm . fromToken ) || ( ! TokensService . areTokensEqual ( prevForm ?. toToken , curForm . toToken ) && curForm . toToken ) ) { this . updateTokensPrices ( curForm ) ; } if ( ! TokensService . areTokensEqual ( prevForm ?. fromToken , curForm . fromToken ) && curForm . fromToken ) { this . updateTokenBalance ( curForm . fromToken ) ; } } ) ; } private setSwapProviderType ( form ) { const { fromBlockchain , toBlockchain , fromToken , toToken } = form ; if ( fromBlockchain === toBlockchain ) { this . swapMode = SWAP_PROVIDER_TYPE . INSTANT_TRADE ; } else if ( ! fromToken || ! toToken ) { if ( ! this . swapMode || this . swapMode === SWAP_PROVIDER_TYPE . INSTANT_TRADE ) { this . swapMode = SWAP_PROVIDER_TYPE . CROSS_CHAIN_ROUTING ; } } else { this . bridgeTokenPairsByBlockchainsArray$ . pipe ( first ( ) ) . subscribe ( bridgeTokenPairsByBlockchainsArray => { const foundBridgeToken = bridgeTokenPairsByBlockchainsArray . find ( tokenPairsByBlockchains => tokenPairsByBlockchains . fromBlockchain === fromBlockchain && tokenPairsByBlockchains . toBlockchain === toBlockchain ) ?. tokenPairs . find ( tokenPair => compareAddresses ( tokenPair . tokenByBlockchain [ fromBlockchain ] . address , fromToken . address ) && compareAddresses ( tokenPair . tokenByBlockchain [ toBlockchain ] . address , toToken . address ) ) ; if ( foundBridgeToken ) { this . swapMode = SWAP_PROVIDER_TYPE . BRIDGE ; } else { this . swapMode = SWAP_PROVIDER_TYPE . CROSS_CHAIN_ROUTING ; } } ) ; } } param blockchain param bridgeTokenPair param tokenAmounts param updatedTokenAmounts return private getUpdatedBridgeToken ( blockchain , bridgeTokenPair , tokenAmounts , updatedTokenAmounts ) { const bridgeToken = bridgeTokenPair . tokenByBlockchain [ blockchain ] ; const foundTokenAmount = tokenAmounts . find ( tokenAmount => tokenAmount . blockchain === blockchain && compareAddresses ( tokenAmount . address , bridgeToken . address ) ) ; if ( ! foundTokenAmount && ! updatedTokenAmounts . find ( tokenAmount => tokenAmount . blockchain === bridgeToken . blockchain && compareAddresses ( tokenAmount . address , bridgeToken . address ) ) ) { if ( this . iframeService . isIframe ) { return null ; } updatedTokenAmounts . push ( { ... bridgeToken , image : bridgeTokenPair . image , rank : 0 , price : 0 , usedInIframe : false , amount : new BigNumber ( 0 ) , favorite : false } ) ; } return { ... foundTokenAmount , ... bridgeToken } ; } param form private updateTokensPrices ( form ) { if ( this . intervalId ) { clearInterval ( this . intervalId ) ; } const update = ( ) => { if ( form ?. fromToken ) { this . tokensService . getAndUpdateTokenPrice ( form . fromToken ) ; } if ( form ?. toToken ) { this . tokensService . getAndUpdateTokenPrice ( form . toToken ) ; } } ; update ( ) ; this . intervalId = setInterval ( update , 15000 ) ; } /**
     * Calls functions to update balance, if needed.
     */ private updateTokenBalance ( fromToken ) { if ( ! fromToken . amount ?. isFinite ( ) ) { this . tokensService . getAndUpdateTokenBalance ( fromToken ) ; } } }