import { BehaviorSubject , combineLatest , Observable , Subscription } from 'rxjs' ; import { TSMap } from 'typescript-map' ; import { linearBackingOffRetry , logger , RadixAccountSystem , RadixAtomObservation , RadixDataAccountSystem , RadixDecryptionAccountSystem , RadixDecryptionProvider , radixHash , RadixMessagingAccountSystem , RadixTokenDefinitionAccountSystem , RadixTransaction , RadixTransferAccountSystem , radixUniverse , sleep } from '../..' ; import { RadixAddress } from '../atommodel' ; import axios from 'axios' ; import { map , catchError } from 'rxjs/operators' ; import RadixTransactionUpdate from './RadixTransactionUpdate' ; import { filter } from 'rxjs/operators' ; const sleepAmountIncrease = 1000 ; export default class RadixAccount { private accountSystems : TSMap < string , RadixAccountSystem > = new TSMap ( ) ; public decryptionSystem : RadixDecryptionAccountSystem ; public transferSystem : RadixTransferAccountSystem ; public dataSystem : RadixDataAccountSystem ; public messagingSystem : RadixMessagingAccountSystem ; public tokenDefinitionSystem : RadixTokenDefinitionAccountSystem ; private processingAtomCounter = new BehaviorSubject ( 0 ) ; private atomObservable : Observable < RadixAtomObservation > ; private subs = new Subscription ( ) ; private sleepAmount = sleepAmountIncrease ; public unsubscribeSubscribers ( ) { this . subs . unsubscribe ( ) ; if ( this . decryptionSystem ) { this . decryptionSystem . unsubscribeSubscribers ( ) ; } if ( this . tokenDefinitionSystem ) { this . tokenDefinitionSystem . unsubscribeSubscribers ( ) ; } if ( this . transferSystem ) { this . transferSystem . unsubscribeSubscribers ( ) ; } if ( this . dataSystem ) { this . dataSystem . unsubscribeSubscribers ( ) ; } if ( this . messagingSystem ) { this . messagingSystem . unsubscribeSubscribers ( ) ; } } param address param plain constructor ( readonly address , plain = false ) { if ( ! plain ) { this . decryptionSystem = new RadixDecryptionAccountSystem ( ) ; this . addAccountSystem ( this . decryptionSystem ) ; this . tokenDefinitionSystem = new RadixTokenDefinitionAccountSystem ( address ) ; this . addAccountSystem ( this . tokenDefinitionSystem ) ; this . transferSystem = new RadixTransferAccountSystem ( address ) ; this . addAccountSystem ( this . transferSystem ) ; this . dataSystem = new RadixDataAccountSystem ( address ) ; this . addAccountSystem ( this . dataSystem ) ; this . messagingSystem = new RadixMessagingAccountSystem ( address ) ; this . addAccountSystem ( this . messagingSystem ) ; } this . atomObservable = radixUniverse . ledger . getAtomObservations ( address ) ; this . subs . add ( this . atomObservable . subscribe ( { next : this . _onAtomReceived , } ) ) ; } param address param plain returns public static fromAddress ( address , plain = false ) { return new RadixAccount ( RadixAddress . fromAddress ( address ) , plain ) ; } param seed param plain returns public static fromSeed ( seed , plain = false ) { const hash = radixHash ( seed ) ; return new RadixAccount ( RadixAddress . fromPrivate ( hash ) , plain ) ; } param decryptionProvider public enableDecryption ( decryptionProvider ) { this . decryptionSystem . decryptionProvider = decryptionProvider ; } /**
     * Get the address of this account
     */ public getAddress ( ) { return this . address . getAddress ( ) ; } param system public addAccountSystem ( system ) { if ( this . accountSystems . has ( system . name ) ) { throw new Error ( `System "${ system . name }" already exists in account, you can only have one of each system per account` ) ; } this . accountSystems . set ( system . name , system ) ; return system ; } param name public removeAccountSystem ( name ) { if ( this . accountSystems . has ( name ) ) { this . accountSystems . delete ( name ) ; } } param name public getSystem ( name ) { if ( this . accountSystems . has ( name ) ) { return this . accountSystems . get ( name ) ; } throw new Error ( `System "${ name }" doesn't exist in account` ) ; } returns public isSynced ( ) { return combineLatest ( [ radixUniverse . ledger . onSynced ( this . atomObservable ) , this . processingAtomCounter . pipe ( map ( ( value ) => value === 0 ) ) ] ) . pipe ( map ( isSynced => isSynced [ 0 ] && isSynced [ 1 ] ) ) ; } private _onAtomReceived = async ( atomObservation ) => { if ( ! this . processingAtomCounter . closed && ! this . processingAtomCounter . isStopped ) { this . processingAtomCounter . next ( this . processingAtomCounter . getValue ( ) + 1 ) ; } else { logger . error ( `☢️ processingAtomCounter closed or stopped` ) ; } atomObservation . processedData = { } ; for ( const system of this . accountSystems . values ( ) ) { await system . processAtomUpdate ( atomObservation ) ; } if ( ! this . processingAtomCounter . closed && ! this . processingAtomCounter . isStopped ) { this . processingAtomCounter . next ( this . processingAtomCounter . getValue ( ) - 1 ) ; } else { logger . error ( `☢️ processingAtomCounter closed or stopped` ) ; } } ; public async requestTestTokensFromFaucetWithLinearBackingOffRetry ( url , maxNumberOfRetries = 10 ) { return linearBackingOffRetry ( getTokensFromFaucetURL . bind ( null , this . address , url ) , maxNumberOfRetries ) ; } } const getTokensFromFaucetURL = async ( radixAddress , url ) : Promise < string > => { const faucetURL = `${ url }/api/v1/getTokens/${ radixAddress . toString ( ) }` ; let errorMsg = `Failed to get tokens from faucet` ; let response ; try { response = await axios . get ( faucetURL ) ; } catch ( e ) { throw new Error ( errorMsg ) ; } if ( response . data ) { const txString = response . data ; if ( typeof txString === 'string' && txString . length > 0 ) { return txString ; } } throw new Error ( errorMsg ) ; } ;